2

我有一个在 IO monad 中运行的函数:

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

现在我决定将它概括为在一些MonadIO m. 我是按照以下方式进行的,bracket用我的scope(你知道图书馆里的一些吗?)重新发明:

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. 我发现了错误并修复了它。liftIO $之前删除就够了scope。但这只是尝试重新编译循环。此错误消息中的何处说明了错误的位置?我看到'fn'有问题。好的,我想了想,有了一个猜测:GHC 从上到下推断类型。liftIO它从使用中推断m应该是IOfn具有一般类型m,所以它是错误的。是否有任何haskell 编译器从上到下进行推断?而且(更重要的是)我可以看到 GHC 为输出中的子表达式推断出的类型吗?

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

4

1 回答 1

4

liftIO :: (MonadIO m) => IO a -> m a采取IO行动,所以说liftIO $ scope ...,你是说scope ...必须有 type IO b。这意味着 to 的参数scope必须使用IOmonad。由于您使用了scopeensure that mmust be IO,因此您可以scope将这种类型视为在上下文中:

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

因此,调用中的s什么liftIO都不做;他们只是从to转换,你不能使用,因为它适用于,而不是。移除固定它,因为它直接在内部运行,而不是在内部运行(不可能,因为在内部运行)并将该动作提升到.scopeIO aIO afnmIOliftIOscopemIOfnmm

于 2012-05-17T14:58:08.557 回答