的文档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图层。