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 的效果。