【发布时间】:2015-07-11 17:30:17
【问题描述】:
这是我之前帖子的后续。 MaybeT and Transactions in runDb
我认为这将是一件简单的事情,但我已经尝试了一天多,但仍然没有取得太大进展。所以想我会放弃并问!
我刚刚在我之前的代码中添加了一个try 函数(来自Control.Exception.Lifted),但我无法获取代码来进行类型检查。 catch 和 handle 等变体也有类似的问题。
eauth <- LiftIO (
try( runDb $ do
ma <- runMaybeT $ do
valid <- ...
case ma of
Just a -> return a
Nothing -> liftIO $ throwIO MyException
) :: IO (Either MyException Auth)
)
case eauth of
Right auth -> return auth
Left _ -> lift $ left err400 { errBody = "Could not create user"}
我的runDb 看起来像这样(我还尝试了一个删除liftIO 的变体):
runDb query = do
pool <- asks getPool
liftIO $ runSqlPool query pool
我收到此错误:
No instance for (Control.Monad.Reader.Class.MonadReader Config IO)
arising from a use of ‘runDb’
In the expression: runDb
In the first argument of ‘try’, namely
‘(runDb
$ do { ma <- runMaybeT ...
我在仆人处理程序中运行,我的返回类型是AppM Auth
type AppM = ReaderT Config (EitherT ServantErr IO)
我尝试了许多举重组合,但似乎没有帮助。我想我会借此机会从头开始弄清楚事情,我也碰壁了。如果有人可以建议您如何得出答案,那对我来说将是非常有启发性的。
这是我的思考过程:
- 我看到
runSqlConn :: MonadBaseControl IO m => SqlPersistT m a -> Connection -> m a - 所以这似乎暗示它将在
IOmonad 中,这意味着try应该可以工作 - 我想检查
MonadBaseControl的定义,它有class MonadBase b m => MonadBaseControl b m | m -> b。在这一点上,我很困惑。这个函数依赖逻辑似乎是建议类型m指示b将是什么,但在前一个b被指定为IO。 - 我检查了
MonadBase,这也没有给我任何线索。 - 我检查了
SqlPersistT,也没有任何线索。 - 我将问题简化为非常简单的问题,例如
result <- liftIO (try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)),并且成功了。所以这个时候我就更加迷茫了。runDb不能在IO中工作,所以我的原始代码不应该同样工作吗?
我认为我可以通过回溯来解决这个问题,但我的 Haskell 知识水平似乎不足以解决问题的根源。欣赏人们是否可以提供逐步指导以得出正确的解决方案。
谢谢!
【问题讨论】:
-
LiftIO是不是错字,应该是liftIO?try也无法更改它正在运行的 monad(runDB没有返回IO,而您正试图让它返回IO)。事实上,看起来try可能对runDB本身也有效。 -
您应该为
runDb提供类型签名。 @Guvante 的评论和我的回答都应该清楚地表明runDB不会返回IO。由于您使用的异常只有一个值,因此Maybe/MaybeTmonad 对于您正在做的事情来说已经足够强大了。请参阅我答案的最后一部分。如果您仍然遇到问题,您应该在runMaybeT块中包含您省略的代码。 -
@Guvante,你说得对,
LiftIO是一个错字,你的观察也是正确的。由于我的类型签名,我最终陷入了兔子洞。
标签: haskell persistent