Pet*_*lák 6 monads haskell typeclass monad-transformers
为什么没有MonadTrans定义为
class MonadTrans t where
lift :: (Monad m, Monad (t m)) => m a -> t m a
-- ^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
而不是当前
Run Code Online (Sandbox Code Playgroud)class MonadTrans t where lift :: Monad m => m a -> t m a
这是Haskell 98(不同于为什么monad变换器不能生成monad的建议?)并确保结果永远是monad.是否有理由允许monad变换器生产不是monad的东西?
bheklilr的回答让我想到了一个monad变换器产生的东西不是monad的例子.一个不是monad的着名例子是ZipList.我们可以制作一个在每个级别运行monadic动作的变体:
import Control.Applicative
import Control.Arrow ((***))
import Control.Monad
import Control.Monad.Trans
-- | A list where each step is produced by a monadic action.
data ListT m a = Nil | Cons (m (a, ListT m a))
Run Code Online (Sandbox Code Playgroud)
这实际上是一个monad流.它可以很容易地制成一个Functor和一个Applicative
instance Monad m => Functor (ListT m) where
fmap f Nil = Nil
fmap f (Cons k) = Cons $ (f *** fmap f) `liftM` k
instance Monad m => Applicative (ListT m) where
pure x = Cons $ return (x, pure x)
Cons mf <*> Cons mx = Cons $ do
(f, fs) <- mf
(x, xs) <- mx
return (f x, fs <*> xs)
_ <*> _ = Nil
Run Code Online (Sandbox Code Playgroud)
但显然不是一个单子.所以我们有一个MonadTrans实例将monad转换成只有aad的东西Applicative.
instance MonadTrans ListT where
lift mx = Cons $ (\x -> (x, lift mx)) `liftM` mx
Run Code Online (Sandbox Code Playgroud)
(这一切让我意识到,ZipSink在管道 - 额外的实验也是一个很好的例子.)
然而,这提出了另一个问题:如果我们想要这样的变形金刚,他们应该遵守哪些法律?法律MonadTrans定义为
Run Code Online (Sandbox Code Playgroud)lift . return = return lift (m >>= f) = lift m >>= (lift . f)
所以在我们的情况下,我们希望有类似的东西
lift (f `liftM` x) = fmap f (lift x)
lift . return = pure
lift (m `ap` f) = lift m <*> lift f
Run Code Online (Sandbox Code Playgroud)
我的猜测是 aMonadTrans将 a 转换Monad为其他内容,而不是将 a 转换Monad为 a Monad。它更通用,因为您可能会编写一些转换 a 的内容,Monad并且可以定义lift,但不能定义>>=和return。由于大多数(如果不是全部)MonadTrans实例最终都是Monads,因此它实际上并不会出现问题,因为编译器仍然可以很好地处理它。