Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么不是';t提升';s返回值被约束为单子?_Haskell_Monads_Typeclass_Monad Transformers - Fatal编程技术网

Haskell 为什么不是';t提升';s返回值被约束为单子?

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。它更一般化

为什么MonadTrans不被定义为

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