Haskell/解决方案/Monad 变换器
外观
1.
liftM
可以用 Monad
方法 return
和 (>>=)
来定义。这些方法不足以定义 lift
,因为内部 monad 中的值如何与基本 monad 和变换器协同工作取决于变换器的类型。特别是,变换器所包装的类型的差异,如 变换器泛滥 部分所说明的那样,使得通用实现变得不可能。
2.
newtype IdentityT m a = IdentityT { runIdentityT :: m a }
instance Monad m => Monad (IdentityT m) where
return x = IdentityT (return x)
(IdentityT m) >>= k = IdentityT $ m >>= runIdentityT . k
instance MonadTrans (IdentityT m) where
lift = IdentityT
1.
state :: MonadState s m => (s -> (a, s)) -> m a
state f = do
s <- get
let (x, s') = f s
put s'
return x
2.
它们并不等价。将 runMaybeT
的类型专门化为与 MaybeT (State s)
一起使用,我们得到
-- runMaybeT :: MaybeT m a -> m (Maybe a)
MaybeT (State s) a -> State s (Maybe a)
对 runStateT
和 StateT s Maybe
执行相同的操作,我们得到
-- runStateT :: StateT s m a -> s -> m (a, s)
StateT s Maybe a -> s -> Maybe (a, s)
在第一种情况下,我们得到一个返回 Maybe a
的 State
计算。在第二种情况下,我们得到一个函数,该函数从类型为 s
的初始状态开始,可能会返回一个结果和一个新状态,也可能不返回,因为结果类型为 Maybe (a, s)
。在 MaybeT (State s)
中的 Nothing
仅破坏返回的结果,而在 StateT s Maybe
中,它也会破坏最终状态。这种比较可以说明一些一般性的论述
- 一般而言,monad 的堆叠顺序很重要。
- 当组合的 monad 被解包时,内部 monad 的效果优先于基本 monad 的效果。
- 错误处理层,例如
MaybeT
和ExceptT
,通常位于变换器堆栈的顶部,以便在发生错误时保留其他涉及的 monad 的效果。