我正在尝试向我的免费monad添加异常处理,但我无法捕获评估者抛出的任何内容。到目前为止,我添加了Except,就像我有其他变形金刚一样:
data TestFunctor a = Test a deriving(Functor)
runTest :: FT TestFunctor (Except Int) a -> Either Int a
runTest = runIdentity . runExceptT . iterT evalTest
evalTest :: TestFunctor (Except Int a) -> Except Int a
evalTest (Test next) = throwError 2 *> next
但在运行时:
> runTest (catchError (throwError 0) (const $ pure ()))
Right ()
> runTest (catchError (liftF $ Test ()) (const $ pure ()))
Left 2
有没有办法在评估者的免费monad catch错误中允许表达式?
我正在使用Control.Monad.Trans.Free.Church
模块。
答案 0 :(得分:4)
构建FT TestFunctor (Except Int)
时,catchError
会在您开始使用runTest
进行解释之前发生。如果我们暂时切换到FreeT
,我们可以看到发生了什么。我们将评估以下所有内容:
throwError 0 :: FreeT TestFunctor (Except Int) ()
catchError (throwError 0) (const $ pure ()) :: FreeT TestFunctor (Except Int) ()
liftF $ Test () :: FreeT TestFunctor (Except Int) ())
catchError (liftF $ Test ()) (const $ pure () :: FreeT TestFunctor (Except Int) ())
当我们抛出错误时,我们会将错误包含在FreeT
。
> throwError 0 :: FreeT TestFunctor (Except Int) ()
FreeT (ExceptT (Identity (Left 0)))
捕获错误会将其替换为免费树
> catchError (throwError 0) (const $ pure ()) :: FreeT TestFunctor (Except Int) ()
FreeT (ExceptT (Identity (Right (Pure ()))))
另一方面,当我们liftF
Test ()
时,它会返回到Right
中的ExceptT
,就像它所拥有的()
一样。
> liftF $ Test () :: FreeT TestFunctor (Except Int) ())
FreeT (ExceptT (Identity (Right (Free (Test (FreeT (ExceptT (Identity (Right (Pure ()))))))))))
| The one above is a return, the one below is a throw
FreeT (ExceptT (Identity (Left 0)))
当你catchError
这个时,它没有做任何事情,它已经成功了。
> catchError (liftF $ Test ()) (const $ pure () :: FreeT TestFunctor (Except Int) ())
FreeT (ExceptT (Identity (Right (Free (Test (FreeT (ExceptT (Identity (Right (Pure ()))))))))))
如果您想在自由树的评估中抛出并捕获错误,请将throw和catch操作添加到您的仿函数可以表示的内容中。
data ExceptOp e a
= Throw e
| Catch a (e -> a)
deriving (Functor)
要解释支持抛出和捕获异常的操作,我们将Throw
替换为throwError
,将Catch
替换为catchError
runTest :: FT (ExceptOp Int) Identity a -> Either Int a
runTest = runIdentity . runExceptT . iterTM evalExcept
evalExcept :: (Monad m) => ExceptOp e (ExceptT e m a) -> ExceptT e m a
evalExcept (Throw e) = throwError e
evalExcept (Catch next f) = catchError next f
这会在我们评估时捕获异常
> runTest (wrap $ Catch (liftF $ Throw 0) (const $ pure ()))
Right ()