为什么Applicative应该是Monad的超类? [英] Why should Applicative be a superclass of Monad?

查看:125
本文介绍了为什么Applicative应该是Monad的超类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

鉴于:

  Applicative m,Monad m => mf :: m(a  - > b),ma :: ma 

它似乎被考虑一条法则:

  mf * ma === do {f < -  mf; a -a;返回(fa)} 

或更简洁:

 (*)=== ap 

控制的文档.Applicative 表示<> 是顺序应用程序,这表明( *)= ap 。这意味着<> 必须从左到右依次评估效果,以便与>> = ...但那感觉不对。 McBride和Paterson的原创文章似乎意味着左边从右至右排序是任意的:


IO monad,实际上任何Monad都可以通过取 pure =
return <*> = AP 我们也可以使用 ap 这个变体以相反的顺序执行$ ​​b $ b的计算,但我们应该保持左转

所以有两个 合法的,非平凡的< / code>< / code>,具有不同的行为。在某些情况下, 这两个派生都是不可取的。

例如,(< *> ;)=== ap law forces Data.Validation 来定义两种不同的数据类型:验证 AccValidation 。前者具有 Monad 实例,类似于 ExceptT 和一个一致的 Applicative 实例有限,因为它在第一个错误后停止。另一方面,后者没有定义 Monad 实例,因此可以自由地实现 Applicative 更有用的是,它会积累错误。

有一些关于此前关于StackOverflow的讨论,但我不认为它真的有问题:

为什么这应该是一个法律吗?



函子,应用程序和单子的其他规则 - 如身份,关联性等 - 表示这些结构的一些基本的数学属性。我们可以使用这些法则实施各种优化,并使用它们来证明我们自己的代码。相反,我感觉像(*)=== ap 法则强加了一个任意的约束,没有相应的好处。

$ b $
$ b

$ b $ p

$ b pre $ newtype LeftA ma = LeftA(ma)
instance Monad m =>应用(LeftA m)其中
pure =返回
mf * ma = do {f <-mf; a -a;返回(f a)}

newtype RightA m a = RightA(m a)
instance Monad m =>应用(RightA m)其中
pure =返回
mf * ma = do {a < - ma; f <-mf;返回(f a)}

我认为正确地捕捉了两者之间的关系,而不会不适当地限制。



所以,从以下几个角度来处理这个问题:


  • 任何其他与 Monad Applicative

  • 有关的法律是否有任何固有的数学按照与 Monad
  • $ b $相同的方式对 Applicative 进行排序的效果原因b
  • GHC或任何其他工具是否执行代码转换,假设/要求此法是真实的?

  • 为什么是 Functor-Applicative-Monad 提议认为这是一件非常好的事情? (引用将在这里非常感谢)



还有一个奖励问题:


  • 另类 MonadPlus 适合所有这些吗?






注意:主要编辑以澄清问题的内容。答案由@duplode发布引用了较早的版本。

解决方案

好吧,我对目前给出的答案并不满意,但我认为附在他们身上的评论更有吸引力。所以我在这里总结一下:






我认为只有一个明智的 Functor fmap f fa = code $> c $ f $>纯f *假设

假设它是唯一的,那么 Functor 应该是 Applicative 的超类。同样,我认为从 Monad 后面只有一个明智的 Functor 实例:

  fmap f fa = fa>> = return。 f 

再说一次, Functor 应该是 Monad 的超类。我所拥有的异议(实际上还有)是从 Monad 有两个明显的 Applicative 实例,在某些特定情况下,甚至更多是合法的;所以为什么要求一个?



pigworker (第一作者在 original Applicative


当然不会,这是一个选择。



(在 twitter ):do-notation is unjust对monad工作的惩罚;我们应得到应用符号




......可以这么说 pure === return (*)=== ap 在强烈意义上并不是法律,例如monad法律是......



LeftA / RightA idea:有c标准图书馆其他地方无法比拟的情况(例如 Sum 产品在 Data.Monoid 中)。与 Applicative 相同的问题是功率与重量的关系太低,无法证明额外的精度/灵活性。


所以,我很高兴看到这个选择明确地陈述了,通过简单的理由证明它使得最常见的情况更容易。\\ b $ b

Given:

Applicative m, Monad m => mf :: m (a -> b), ma :: m a

it seems to be considered a law that:

mf <*> ma === do { f <- mf; a <- ma; return (f a) }

or more concisely:

(<*>) === ap

The documentation for Control.Applicative says that <*> is "sequential application," and that suggests that (<*>) = ap. This means that <*> must evaluate effects sequentially from left to right, for consistency with >>=... But that feels wrong. McBride and Paterson's original paper seems to imply that the left-to-right sequencing is arbitrary:

The IO monad, and indeed any Monad, can be made Applicative by taking pure = return and <*> = ap. We could alternatively use the variant of ap that performs the computations in the opposite order, but we shall keep to the left-to-right order in this paper.

So there are two lawful, non-trivial derivations for <*> that follow from >>= and return, with distinct behavior. And in some cases, neither of these two derivations are desirable.

For example, the (<*>) === ap law forces Data.Validation to define two distinct data types: Validation and AccValidation. The former has a Monad instance similar to ExceptT, and a consistent Applicative instance which is of limited utility, since it stops after the first error. The latter, on the other hand, doesn't define a Monad instance, and is therefore free to implement an Applicative that, much more usefully, accumulates errors.

There's been some discussion about this previously on StackOverflow, but I don't think it really got to the meat of the question:

Why should this be a law?

The other laws for functors, applicatives and monads—such as identity, associativity, etc.—express some fundamental, mathematical properties of those structures. We can implement various optimizations using these laws and prove things about our own code using them. In contrast, it feels to me like the (<*>) === ap law imposes an arbitrary constraint with no corresponding benefit.

For what it's worth, I'd prefer to ditch the law in favor of something like this:

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) }

I think that correctly captures the relationship between the two, without unduly constraining either.

So, a few angles to approach the question from:

  • Are there any other laws relating Monad and Applicative?
  • Is there any inherent mathematical reason for effects to sequence for Applicative in the same way that they do for Monad?
  • Does GHC or any other tool perform code transformations that assume/require this law to be true?
  • Why is the Functor-Applicative-Monad proposal considered such an overwhelmingly good thing? (Citations would be much appreciated here).

And one bonus question:

  • How do Alternative and MonadPlus fit in to all this?

Note: major edit to clarify the meat of the question. Answer posted by @duplode quotes an earlier version.

解决方案

Well, I'm not terribly satisfied with the answers given so far, but I think the comments attached to them are a bit more compelling. So I'll summarize here:


I think there's only one sensible Functor instance that follows from Applicative:

fmap f fa = pure f <*> fa

Assuming that's unique, it makes sense that Functor should be a superclass of Applicative, with that law. Likewise, I think there's only one sensible Functor instance that follows from Monad:

fmap f fa = fa >>= return . f

So again, it makes sense that Functor should be a superclass of Monad. The objection I had (and, really, still have) is that there are two sensible Applicative instances that follow from Monad and, in some specific instances, even more that are lawful; so why mandate one?

pigworker (first author on the original Applicative paper) writes:

"Of course it doesn't follow. It's a choice."

(on twitter): "do-notation is unjust punishment for working in a monad; we deserve applicative notation"

duplode similarly writes:

"... it is fair to say that pure === return and (<*>) === ap aren't laws in the strong sense that e.g. the monad laws are so ..."

"On the LeftA/RightA idea: there are comparable cases elsewhere in the standard libraries (e.g. Sum and Product in Data.Monoid). The problem of doing the same with Applicative is that the power-to-weight relation is too low to justify the extra precision/flexibility. The newtypes would make applicative style a lot less pleasant to use."

So, I'm happy to see that choice stated explicitly, justified by the simple reasoning that it makes the most common cases easier.

这篇关于为什么Applicative应该是Monad的超类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆