docs for Control.Monad.Trans.Error提供了合并两个单体的示例:
type ErrorWithIO e a = ErrorT e IO a
==> ErrorT (IO (Either e a))我发现这是违反直觉的:尽管ErrorT应该包装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也被封装在一个函数中。
让事情变得更令人困惑的是,如果单子是相反的组合的话:
type ErrorWithState e s a = ErrorT e (State s) a
==> ErrorT (StateT (s -> (Either e a, s)))“外部”仍然是一个函数;它不会产生类似于Either e (s -> (a, s))的东西,其中状态函数嵌套在错误类型中。
我相信所有这些都有一些潜在的逻辑一致性,但我不太明白。因此,我发现很难思考把一个单体和另一个单体结合在一起意味着什么,即使我不难理解每个单体单独意味着什么。
有人能启发我吗?
(脚注:,我在用Identity编写ErrorT,以便StateWithError和ErrorWithState相互一致,以便于说明)。通常,我只使用StateWithError s e a = StateT s (Either e) a,放弃ErrorT层。
发布于 2011-08-17 05:36:55
--我发现这是违反直觉的:尽管ErrorT应该是包装IO,但看起来错误信息已经被注入IO操作的结果类型中。
Monad变压器一般不会“包装”它们所使用的单片,至少在任何明显的意义上都是如此。把它当作“包装”在我的脑海中意味着函子的组成,这就是这里没有发生的事情。
为了说明,State s和Maybe的函子组合(扩展了定义)如下所示:
newtype StateMaybe s a = StateMaybe (s -> (Maybe a, s)) -- == State s (Maybe a)
newtype MaybeState s a = MaybeState (Maybe (s -> (a, s))) -- == Maybe (State s a)注意,在第一种情况下,State行为正常,而Nothing不影响状态值;在第二种情况下,我们要么有一个普通的State函数,要么什么也没有。无论是哪种情况,这两个单子的特征行为实际上都没有结合起来。这并不令人惊讶,因为毕竟,通过简单地将一个monad作为常规值在另一个monad中使用就可以得到相同的结果。
将其与StateT s Maybe进行比较
newtype StateTMaybe s a = StateTMaybe (s -> Maybe (a, s))在这种情况下,两者是交织在一起的;对于State来说,事情以正常的方式进行,除非我们碰到一个Nothing,在这种情况下,计算将被中止。这从根本上不同于上述情况,这就是为什么单台变压器一开始就存在的原因--天真地组合它们不需要任何特殊的机器,因为它们彼此独立运作。
就“外部”而言,从某种意义上说,将“外部”变压器视为其行为在某种意义上是“优先”的,在某种意义上说,在处理单一价值观念时,“内部”变压器只会看到一切照旧。请注意,这就是为什么IO总是最内敛的原因--它不会让其他任何东西进入其业务,而假设的IOT转换器将被迫允许包装的单机拉出各种诡计,比如复制或丢弃RealWorld令牌。
StateT和ReaderT都将“内部”单值放在函数的结果周围;在获得转换后的monad.MaybeT和ErrorT之前,您必须提供一个状态值或环境,它们都在转换后的monad中滑动,确保它能够以通常的方式运行,但可能不是present.Writer的值是完全被动的,它只是将自己附加到monad中的值上,因为它不会影响all.ContT的行为,只通过包装结果类型来推迟处理转换后的monad。这是有点手摇,但是呃,单台变压器是一种特别的,令人困惑的开始,唉。我不知道是否有任何整齐的理论上的理由,为特定的选择,除了事实,他们的工作,并做你通常希望的组合(而不是组合)的两个单子。
因此,我发现很难思考将一个单体和另一个单体结合在一起意味着什么,即使我没有困难地理解每个单体单独意味着什么。
是啊,恐怕这听起来像是在期待什么。
发布于 2011-08-17 08:54:36
考虑一下,如果ErrorT是按照您想象的方式定义的,那么会发生什么。如何对失败的IO操作进行编码?使用Either e (IO a),当操作失败时,您不能给出一个Left值,因为当您到达该操作时,已经清楚地知道它是一个Right值,否则它将不是一个操作。
然而,对于IO (Either e a),情况并非如此。整个过程现在是一个IO操作,可以返回一个Left值来表示错误。正如其他人所指出的,不要将单台变压器视为包装器。而是把它们看作是函数。他们把一个单曲变成另一个单曲。它们改变了单簧管。
https://stackoverflow.com/questions/7088085
复制相似问题