为什么电梯的返回值不能限制为monad?

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)

而不是当前

class MonadTrans t where
    lift :: Monad m => m a -> t m a
Run Code Online (Sandbox Code Playgroud)

这是Haskell 98(不同于为什么monad变换器不能生成monad的建议)并确保结果永远是monad.是否有理由允许monad变换器生产不是monad的东西?

Pet*_*lák 6

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定义为

lift . return = return
lift (m >>= f) = lift m >>= (lift . f)
Run Code Online (Sandbox Code Playgroud)

所以在我们的情况下,我们希望有类似的东西

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)


bhe*_*ilr 4

我的猜测是 aMonadTrans将 a 转换Monad为其他内容,而不是将 a 转换Monad为 a Monad。它更通用,因为您可能会编写一些转换 a 的内容,Monad并且可以定义lift,但不能定义>>=return。由于大多数(如果不是全部)MonadTrans实例最终都是Monads,因此它实际上并不会出现问题,因为编译器仍然可以很好地处理它。