2012-05-17 59 views
2

我曾在IO单子运行的函数:编译错误,而推广功能 - 复杂的错误消息

withDB :: (forall c. IConnection c => c -> IO b) -> IO b 
withDB fn = bracket (connectSqlite3 "int/db.sqlite3") disconnect fn 

现在我决定来概括它在一些MonadIO m运行。我做到了下面的方式,重新发明与bracketscope(你知道一些从图书馆吗?):

scope :: MonadIO m => m a -> (a -> m b) -> (a -> m c) -> m c 
scope before after action = do 
    x <- before 
    r <- action x 
    _ <- after x 
    return r 

withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
withDB fn = liftIO $ scope 
         (liftIO $ connectSqlite3 "int/db.sqlite3") 
         (\x -> liftIO $ disconnect x) fn 

我得到了错误:

Could not deduce (m ~ IO) 
from the context (MonadIO m) 
    bound by the type signature for 
    withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
    at src\... 
    'm' is a rigid type variable bound by 
    the signature for 
     withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b 
Expected type: IO b 
    Actual type: m b 
In the third argument of 'scope' namely 'fn' 
In the second argument of '($)', namely 
    'scope 
    (liftIO $ connectSqlite3 "int/db.sqlite3") 
    (\x -> liftIO $ disconnect x) 
    fn' 

现在我的问题:

  1. 什么意思m〜IO?前两行错误是什么意思?另外,我在haskell代码中看到了这个构造,但是无法找到它。延期?什么是刚性类型变量?

  2. 我发现错误并修复了它。在scope之前删除liftIO $就足够了。但这只是尝试重新编译循环。 其中在这个错误消息中讲述了错误的地方?我发现'fn'有问题。好吧,我想了一下,猜测一下:GHC推断自上而下的类型。而从liftIO推断,m应该是IO,但fn有一般类型m所以它是错误的。 是否有任何 haskell编译器从上到下推断? (更重要的)我可以看到GHC推断输出中的子表达式的类型吗?

感谢您阅读这个长长的问题!

+1

只是FYI你的'范围'功能并不完全是'支架'做的。 'bracket'不仅将计算包装到资源分配/释放的“括号”中,还处理异常,即使在异常情况下也释放资源。你的'scope'函数将会退出,而不是释放资源。也许这在这种情况下并不重要,但仍然如此。 –

+0

通用MonadIO是否可以处理异常? – demi

+0

我认为你可以用'liftIO'管理这个。毕竟它有'IO a - > m a'类型,所有异常处理例程都有'IO'类型。你可以在这里找到所有与异常相关的函数:http://hackage.haskell.org/packages/archive/base/4.5.0.0/doc/html/Control-Exception.html –

回答

4

liftIO :: (MonadIO m) => IO a -> m a需要一个IO动作,所以说liftIO $ scope ...,你说scope ...必须有类型IO b。这意味着scope的参数必须使用IO单子。由于您的scope使用确保m必须IO,你能想到的scope具有这种类型的上下文:

scope :: IO a -> (a -> IO b) -> (a -> IO c) -> IO b 

正因为如此,在liftIO小号scope调用什么都不做;他们只是从IO a转换为IO a,并且您不能使用fn,因为它在m而不是IO。删除liftIO已修复它,因为它直接在m内部运行scope,而不是在IO(不可能,因为fnm中运行)运行它并将该操作解除为m

+0

好的,所以任何类型推断都是从最外层的表达到内层(即从表达树的根到叶,而不是从叶到根)?而且〜IO意味着什么? – demi

+4

'm〜IO'表示它试图证明'm'等于'IO',但它不是(必然)。 –

+0

@demi:不一定关于哪种方向类型推理的工作方式,而是约束条件:使用'liftIO' *需要*范围...'具有'IO b'类型。正如路易斯所说,“无法推断出(a〜b)'”意味着你的代码需要'a'和'b'才能工作,但它们并不一定。 – ehird