Haskell 为什么不是';t提升';s返回值被约束为单子?
为什么MonadTrans不被定义为Haskell 为什么不是';t提升';s返回值被约束为单子?,haskell,monads,typeclass,monad-transformers,Haskell,Monads,Typeclass,Monad Transformers,为什么MonadTrans不被定义为 class MonadTrans t where lift :: (Monad m, Monad (t m)) => m a -> t m a -- ^^^^^^^^^^^ 而不是当前 这是Haskell 98(与中的建议不同),确保结果始终是单子。有没有一个原因可以让单子转换器产生非单子的东西?我的猜测是MonadTrans将monad转换成其他东西,而不是将monad转换成monad。它更一般化
class MonadTrans t where
lift :: (Monad m, Monad (t m)) => m a -> t m a
-- ^^^^^^^^^^^
而不是当前
这是Haskell 98(与中的建议不同),确保结果始终是单子。有没有一个原因可以让单子转换器产生非单子的东西?我的猜测是
MonadTrans
将monad
转换成其他东西,而不是将monad
转换成monad
。它更一般化,因为您可能会编写一些东西来转换单子
,您可以定义提升
,但不能定义>=
和返回
。由于大多数(如果不是全部)MonadTrans
实例最终都是Monad
s,这并不是一个真正的问题,因为编译器仍然可以很好地处理它。bheklillr的回答让我想到了一个示例,其中Monad转换器生成的东西不是Monad。非单子的一个著名例子是ZipList
。我们可以制作一个变体,在每个级别运行一元操作:
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))
这实际上是一个单子流。它可以很容易地变成一个函子
和一个应用程序
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
(这整件事让我意识到,一个实验性的管道内额外设备也是一个很好的例子。)
然而,这提出了另一个问题:如果我们想要这样的变压器,他们应该遵守什么法律?
MonadTrans
的法律定义如下
class MonadTrans t where
lift :: (Monad m, Monad (t m)) => m a -> t m a
-- ^^^^^^^^^^^
因此,在我们的情况下,我们可以希望
lift (f `liftM` x) = fmap f (lift x)
lift . return = pure
lift (m `ap` f) = lift m <*> lift f
lift(f`liftM`x)=fmap f(lift x)
举起返回=纯
升力(m`ap`f)=升力m升力f
我不同意另外两个答案,即结果应该是单子。原因是,否则就没有什么合理的法律应该遵守
lift
应该是一个单子态射,这意味着它应该遵守以下两条定律:
lift (return x) = return x
lift (m >>= f) = lift m >>= \r -> lift (f r)
当您意识到这些定律是两个Kleisli类别之间的函子定律时,它们就更有意义了:
-- i.e. "fmap id = id"
(lift .) return = return
-- i.e. "fmap (f . g) = fmap f . fmap g"
(lift .) (f >=> g) = (lift .) f >=> (lift .) g
但是,如果您不将输出约束为monad,那么这些规则将不再有效,并且您没有合理的方法来验证您是否正确实现了lift
我怀疑真正的原因是让类haskell 98为什么要确保结果是monad?这将严格地说不那么笼统,我看不出它有什么好处。@JohnL主要是因为它们是用一元术语表示的,所以结果必须是一元。如果不是,法律甚至无法表达。但是贝克利尔在他的回答中提出了一个很好的观点,基于此,我提出了一个生成
Monad
->Applicative
转换器的方法。然而,这意味着我们需要制定一套不同的法律。没有个人知识,但我想这就是原因。您可能希望为不是Monad
@JohnL的Functor
或Applicative
创建一个MonadTrans
实例,这似乎是合理的。我没有引用任何源代码,因为我不知道任何源代码(因此,如果有任何人拥有该文档,请随意发布),但MonadTrans只在单个monad上运行是有意义的,而不是在两个monad上运行。请注意,如果将ListT
封装在最后一个monad层中,那么它就是一个monad,具体来说。@GabrielGonzalez是的,记住ListT是我的灵感。但是我的ListT
定义了pure
和
不同,就像它与[]
不同一样。所以它没有一个有效的monad实例。我对提升的想法是:(monad m,monad(tm))=>ma->tma似乎是H98。因此,您建议对于ZipListT
之类的东西,我们应该使用另一个类似lift::(applicative f)=>fa->tfa
?@PetrPudlák我不知道缺少约束的真正原因,所以我只是猜测。应用型变换器会遵守什么定律?我想应用型变换器应该遵守应用型定律,而函子变换器只需要遵守函子定律。虽然它很可能是一个尖函子,所以lift。pure=pure
仍应有效。“我想这就足够了。”加布里埃尔·冈萨雷斯在我自己的回答的最后,我提出了一些定律(只是猜测)。“佩特普德拉克,我喜欢这些定律。只需注意,第一定律是自动为真的,因为参数,但其他两个很好。
lift (return x) = return x
lift (m >>= f) = lift m >>= \r -> lift (f r)
-- i.e. "fmap id = id"
(lift .) return = return
-- i.e. "fmap (f . g) = fmap f . fmap g"
(lift .) (f >=> g) = (lift .) f >=> (lift .) g