我有一个在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'
现在我的问题是:
m~IO是什么意思?前两行错误说的是什么?另外,我在haskell代码中看到了这个〜构造,但无法找到它是什么。延期?什么是刚性类型变量?
我发现错误并修复了它。在liftIO $
之前删除scope
就足够了。但它只是尝试重新编译循环。 哪里在此错误消息中告知错误的位置?我发现“fn&#39;”出了问题。好吧,我想了一下并猜了一下:GHC从上到下推断出类型。并且使用liftIO
推断m
应该是IO
但fn
具有一般类型m
,因此它是错误的。 任何 haskell编译器是从上到下推断出来的吗?而且(更重要的是)我可以看到GHC推断输出中的子表达式的类型吗?
感谢您阅读这个长期的问题!
答案 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
中运行它(不可能,因为m
在m
中运行})并将该动作提升为{{1}}。