在推广函数时编译错误 - 复杂的错误消息

时间:2012-05-17 14:40:37

标签: haskell ghc

我有一个在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&#39;”出了问题。好吧,我想了一下并猜了一下:GHC从上到下推断出类型。并且使用liftIO推断m应该是IOfn具有一般类型m,因此它是错误的。 任何 haskell编译器是从上到下推断出来的吗?而且(更重要的是)我可以看到GHC推断输出中的子表达式的类型吗?

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

1 个答案:

答案 0 :(得分:4)

liftIO :: (MonadIO m) => IO a -> m a会执行IO操作,因此,通过说liftIO $ scope ...,您说scope ...必须具有IO b类型。这意味着scope的参数必须使用IO monad。由于您使用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。正在删除scope修复它,因为它在m内直接运行IO,而不是在fn中运行它(不可能,因为mm中运行})并将该动作提升为{{1}}。

相关问题