使用exceptions
包时,我对一些特殊的行为感到吃惊。
我有一个看起来像的函数:
testme ::(MonadThrow m , Monad O m, Applicative m) => FilePath -> Text -> m ()
testme fp t = testfoo fp t >> testbar fp t
与
testfoo :: (MonadThrow m , MonadIO m, Applicative m) => FilePath -> Text -> m ()
testbar :: (MonadThrow m , MonadIO m, Applicative m) => FilePath -> Text -> m ()
另一个模块将捕获这里抛出的所有特定异常:
catching _PrettyError
(do {testCatalog workingdir c; return stm})
(return . S.Left)
_PrettyError定义如下:
_PrettyError :: Prism' SomeException PrettyError
_PrettyError = prism' toException fromException
我的第一个惊喜来自于如果我更改testme
:
testme = testfoo >> testbar
Ghc拒绝编译,因为它无法推断出MonadThrow。
尝试此签名:
testme :: FilePath -> Text -> IO ()
testme = testfoo >> testbar
Ghc仍然不想推断MonadThrow
,我觉得更令人惊讶。
我现在正在进行另一种类型的签名更改:
testfoo :: FilePath -> Text -> IO ()
testbar :: FilePath -> Text -> IO ()
并且...它确实编译但是在完全困惑的时候我意识到只有来自第二个调用(testbar)的异常才会被catching
捕获。
testfoo
抛出的那些只是以某种方式丢失......
我很惊讶改变签名会彻底改变异常处理。
The commit of the real code is here(附注)。
当前代码确实按预期工作。
我的问题是为什么从显式约束到特定IO monad的签名更改会导致这种意外行为(>>
的第一项中的例外不再被捕获)。我的理解是,在这两种情况下IO
monad都在起作用。
感谢您的帮助。
答案 0 :(得分:5)
您可能需要testme fp t = testfoo fp t >> testbar fp t
而不是testme = testfoo >> testbar
。在后者中,(>>)
来自(->) r
实例,这让您感到惊讶:f >> g
与g
monad的(->) r
相同(但是让你的电脑变得更热一些。)