[英]Why should Applicative be a superclass of Monad?
鉴于:
Applicative m, Monad m => mf :: m (a -> b), ma :: m a
它似乎被认为是一项法律:
mf <*> ma === do { f <- mf; a <- ma; return (f a) }
或者更简洁:
(<*>) === ap
Control.Applicative
的文档说<*>
是“顺序应用程序”,这表明(<*>) = ap
。 这意味着<*>
必须从左到右依次评估效果,以便与>>=
...保持一致但是这感觉不对。 McBride和Paterson的原始论文似乎暗示从左到右的排序是任意的:
IO monad,实际上任何Monad,都可以通过
pure
=return
和<*>
=ap
。 我们也可以使用ap
的变量以相反的顺序执行计算 ,但我们将在本文中保持从左到右的顺序。
因此,对于<*>
有两个合法的,非平凡的推导,其来自>>=
并return
,具有不同的行为。 在某些情况下, 无论这两个推导是可取的。
例如, (<*>) === ap
法强制Data.Validation定义两种不同的数据类型: Validation
和AccValidation
。 前者具有类似于ExceptT的Monad
实例,以及具有有限效用的一致Applicative
实例,因为它在第一个错误之后停止。 另一方面,后者没有定义Monad
实例,因此可以自由地实现一个更有用的累积错误的Applicative
。
之前在StackOverflow上有过一些关于这个问题的讨论 ,但我认为它并没有真正解决问题:
函子,应用程序和单子的其他定律 - 例如同一性,相关性等 - 表达了这些结构的一些基本的数学属性。 我们可以使用这些定律实现各种优化,并使用它们来证明我们自己的代码。 相比之下,我感觉像(<*>) === ap
法则强加任意约束而没有相应的好处。
对于它的价值,我宁愿放弃法律支持这样的事情:
newtype LeftA m a = LeftA (m a)
instance Monad m => Applicative (LeftA m) where
pure = return
mf <*> ma = do { f <- mf; a <- ma; return (f a) }
newtype RightA m a = RightA (m a)
instance Monad m => Applicative (RightA m) where
pure = return
mf <*> ma = do { a <- ma; f <- mf; return (f a) }
我认为这正确地捕捉了两者之间的关系,而没有过度约束。
因此,从以下几个角度来处理问题:
Monad
和Applicative
法律? Applicative
进行排序效果,就像它们对Monad
? 还有一个奖金问题:
Alternative
和MonadPlus
如何适应这一切? 注意:主要编辑以澄清问题的内容。 @duplode发布的答案引用了早期版本。
好吧,我对目前给出的答案并不十分满意,但我认为附加的评论更具吸引力。 所以我在这里总结一下:
我认为Applicative
只有一个合理的Functor
实例:
fmap f fa = pure f <*> fa
假设这是独一无二的,那么Functor
应该是Applicative
的超类,并且具有该定律。 同样,我认为Monad
只有一个明智的Functor
实例:
fmap f fa = fa >>= return . f
再说一次, Functor
应该是Monad
的超类是有道理的。 我曾经(并且,实际上,仍有)的反对意见是, Monad
有两个合理的Applicative
实例,在某些特定情况下,甚至更合法; 为什么要一个?
pigworker ( 原Applicative
文件的第一作者)写道:
“当然不会跟随。这是一个选择。”
(在Twitter上 ):“在Monad中工作是一种不公正的惩罚;我们应该得到适用的注释”
duplode同样写道:
“...可以公平地说,
pure === return
和(<*>) === ap
不是强烈意义上的法律,例如monad法则是如此......”“关于
LeftA
/RightA
想法:标准库中的其他地方也存在类似的情况(例如,Data.Monoid
Sum
和Product
)。与Applicative
一样的问题是权重关系太低而无法证明额外的精确性/灵活性。新类型会使应用风格的使用变得不那么令人愉快。“
所以,我很高兴看到这个选择明确说明,通过简单的推理证明它使最常见的情况更容易。
除此之外,你问为什么Functor-Applicative-Monad
提案是一件好事。 一个原因是因为缺乏统一意味着API有很多重复。 考虑标准的Control.Monad
模块。 以下是该模块中基本上使用Monad
( MonadPlus
没有)约束的MonadPlus
:
(>>=) fail (=<<) (>=>) (<=<) join foldM foldM_
以下是该模块中的函数,其中Monad
/ MonadPlus
约束可以轻易地告诉我们放宽到Applicative
/ Alternative
:
(>>) return mzero mplus mapM mapM_ forM forM_ sequence sequence_ forever
msum filterM mapAndUnzipM zipWithM zipWithM_ replicateM replicateM_ guard
when unless liftM liftM2 liftM3 liftM4 liftM5 ap
许多在后一组中确实有Applicative
或Alternative
版本,在任一Control.Applicative
, Data.Foldable
或Data.Traversable
-但为什么需要学习摆在首位所有的重复?
并且在我自己的(可能是错误的)直觉中,给定
pure f <*> ma <*> mb
,不需要任何预定的排序,因为这些值都不相互依赖。
值没有,但效果确实如此。 (<*>) :: t (a -> b) -> ta -> tb
意味着你必须以某种方式组合参数的效果才能获得整体效果。 组合是否可交换取决于实例的定义方式。 例如, Maybe
的实例是可交换的,而列表的默认“交叉连接”实例则不是。 因此,有些情况下您无法避免强加某些订单。
Monad和Applicative有哪些法律(如果有的话)?
虽然可以公平地说pure === return
和(<*>) === ap
(引用Control.Applicative
)并不是强烈意义上的法律,例如monad法则是如此,它们有助于保持实例不足为奇。 鉴于每个Monad
都会产生一个Applicative
实例(实际上是两个实例,正如你所指出的那样), Applicative
的实际实例与Monad
给出的实际匹配是很自然的。 至于从左到右的惯例,遵循ap
和liftM2
的顺序 (当引入Applicative
时已经存在,并且反映了由(>>=)
强加的顺序)是一个明智的决定。 (注意,如果我们暂时忽略了多少(>>=)
在实践中很重要,那么相反的选择也是可以防御的,因为它会使(<*>)
和(=<<)
具有类似的类型,序列效果顺序相同。)
GHC或任何其他工具是否执行代码转换,假设/要求此法律为真?
鉴于Applicative
甚至不是Monad
的超类( 尚未 ),这听起来不太可能。 然而,这些“法则”允许代码的读者进行转换,这同样重要。
注意:如果你需要在Applicative
实例中反转效果的顺序,就像Gabriel Gonzalez指出的那样,有Control.Applicative.Backwards
。 此外, (<**>)
翻转参数但仍然从左到右排序效果,因此它也可用于反向排序。 类似地, (<*)
不是flip (*>)
,因为两个序列效果都是从左到右。
只是为了记录,标题中问题的答案是:考虑
sequenceA :: Applicative f, Traversable t => t (f a) -> f (t a)
join :: Monad m => m (m a) -> m a
什么是联接类型join . sequenceA
join . sequenceA
?
Monad m, Traversable m => m (ma) -> ma
Applicative m, Monad m, Traversable m => m (ma) -> ma
join . sequenceA
, join . sequenceA
join . sequenceA
是一种人为的情况,但肯定有需要 monad的情况,但你也想使用Applicative
操作<*>
, *>
, <*
, <**>
等。然后:
Applicative
名称(恕我直言)比传统的monad操作更好。 ap
, >>
, <<
等等,很烦人(“哦,你不能在那里使用<*>
,那是Monad
不是一个Applicative
”;“哦,你必须使用<*>
在那里,这是一个Applicative
不是一个Monad
“)。 >>
和*>
做不同的事情,那么你实际上不能使用Applicative
语法,因为它会做你不期望的事情。 所以,实际上,对每个与它兼容的Monad
(在(<*>) = ap
意义上)都有一个Applicative
是一个非常好的主意。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.