的文档Control.Monad.Trans.Error
提供了这个组合两个 monad 的例子:
type ErrorWithIO e a = ErrorT e IO a
==> ErrorT (IO (Either e a))
我发现这有悖常理:即使ErrorT
据说是wrapping IO
,看起来错误信息已被注入到IO 操作的结果类型中。我原以为会是
==> ErrorT (Either e (IO a))
基于“包装”一词的通常含义。
为了使事情更加混乱,StateT
每个都有一些:
type MyError e = ErrorT e Identity -- (see footnote)
type StateWithError s e a = StateT s (MyError e) a
==> StateT (s -> ErrorT (Either e (a, s)))
状态类型s
已经注入到Either
'Right
端,但整体Either
也被包裹在一个函数中。
更令人困惑的是,如果将 monad 反过来组合:
type ErrorWithState e s a = ErrorT e (State s) a
==> ErrorT (StateT (s -> (Either e a, s)))
“外部”仍然是一个函数;它不会产生类似的东西Either e (s -> (a, s))
,其中状态函数嵌套在错误类型中。
我确信这一切都有一些潜在的逻辑一致性,但我不太明白。因此,我发现很难思考将一个 monad 与另一个结合起来意味着什么,即使我可以毫不费力地理解每个 monad 各自的含义。
有人可以启发我吗?
(脚注:为了说明的目的,我正在编写ErrorT
这样Identity
并且StateWithError
彼此ErrorWithState
一致。通常我只是使用StateWithError s e a = StateT s (Either e) a
并放弃该ErrorT
图层。