mer*_*ict 43 monads haskell applicative fam-proposal
鉴于:
Applicative m, Monad m => mf :: m (a -> b), ma :: m a
Run Code Online (Sandbox Code Playgroud)
它似乎被认为是一项法律:
mf <*> ma === do { f <- mf; a <- ma; return (f a) }
Run Code Online (Sandbox Code Playgroud)
或者更简洁:
(<*>) === ap
Run Code Online (Sandbox Code Playgroud)
说的文档Control.Applicative<*>是"顺序应用程序",这表明了这一点(<*>) = ap.这意味着<*>必须从左到右依次评估效果,以保持一致>>=...但这感觉不对.McBride和Paterson的原始论文似乎暗示从左到右的排序是任意的:
IO monad,实际上任何Monad,都可以通过取
pure=return和<*>=来实现ap.我们也可以使用它的变量ap以相反的顺序执行计算,但我们将在本文中保持从左到右的顺序.
因此,有两个合法的,非平凡的推导<*>来源于>>=并且return具有不同的行为.在某些情况下,既没有这两个推导是可取的.
例如,(<*>) === ap法律强制Data.Validation定义两种不同的数据类型:Validation和AccValidation.前者有一个Monad类似于ExceptT的实例,以及一个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) }
Run Code Online (Sandbox Code Playgroud)
我认为这正确地捕捉了两者之间的关系,而没有过度约束.
因此,从以下几个角度来处理问题:
Monad和Applicative?Applicative使它们以与它们相同的方式进行排序Monad?还有一个奖金问题:
Alternative并MonadPlus适合这一切?注意:主要编辑以澄清问题的内容.@duplode发布的答案引用了早期版本.
mer*_*ict 10
好吧,我对目前给出的答案并不十分满意,但我认为附加在他们身上的评论更具吸引力.所以我在这里总结一下:
我认为只有一个合理的Functor例子来自Applicative:
fmap f fa = pure f <*> fa
Run Code Online (Sandbox Code Playgroud)
假设这是独一无二的,那么根据该法则Functor应该是超类Applicative.同样,我认为只有一个合理的Functor实例来自Monad:
fmap f fa = fa >>= return . f
Run Code Online (Sandbox Code Playgroud)
所以,再次,它Functor应该是一个超类Monad.我曾经(并且,实际上,仍有)的反对意见是,有两个合理的Applicative实例Monad,在某些特定情况下,甚至更多是合法的; 为什么要一个?
pigworker(原始Applicative论文的第一作者)写道:
"当然不会跟随.这是一个选择."
(在Twitter上):"在Monad中工作是一种不公正的惩罚;我们应该得到适用的注释"
duplode同样写道:
"...可以公平地说,
pure === return并且(<*>) === ap不是强烈意义上的法律,例如monad法则是如此......""关于
LeftA/RightA想法:标准库中的其他地方也有类似的情况(例如Sum和ProductinData.Monoid).做同样的问题Applicative是功率 - 权重关系太低而无法证明额外的精度/灵活性.会使应用风格变得不那么令人愉快."
所以,我很高兴看到这个选择明确说明,通过简单的推理证明它使最常见的情况更容易.
除其他外,你问为什么Functor-Applicative-Monad提案是好事.一个原因是因为缺乏统一意味着API有很多重复.考虑标准Control.Monad模块.以下是该模块中基本上使用Monad(没有for MonadPlus)约束的函数:
(>>=) fail (=<<) (>=>) (<=<) join foldM foldM_
Run Code Online (Sandbox Code Playgroud)
以下是该模块中的函数,其中一个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
Run Code Online (Sandbox Code Playgroud)
许多在后一组中也有Applicative或Alternative版本,在任Control.Applicative,Data.Foldable或Data.Traversable-但为什么需要学习摆在首位所有的重复?
在我自己(也许是错误的)直觉中,给定的
pure f <*> ma <*> mb,不需要任何预定的排序,因为没有一个值相互依赖.
值没有,但效果确实如此.(<*>) :: t (a -> b) -> t a -> t b意味着你必须以某种方式结合参数的效果才能获得整体效果.组合是否可交换取决于实例的定义方式.例如,实例Maybe是可交换的,而列表的默认"交叉连接"实例则不是.因此,有些情况下您无法避免强加某些订单.
Monad和Applicative有哪些法律(如果有的话)?
虽然可以公平地说,pure === return并且(<*>) === ap(引用Control.Applicative)不是强烈意义上的法律,例如monad法则是如此,它们有助于保持实例不足为奇.鉴于每个Monad都会产生一个实例Applicative(实际上是两个实例,正如你所指出的那样),实际的Applicative匹配实例是很自然的Monad.至于左到右的惯例,下面的顺序ap和liftM2(已存在的回来时Applicative被引入,并反映所规定的顺序(>>=))是一个明智的决定.(注意,如果我们暂时忽略了(>>=)实际中有多重要,那么相反的选择也是可以防御的,就像它会产生的那样,(<*>)并且(=<<)它们具有类似的类型,序列效果的顺序相同.)
GHC或任何其他工具是否执行代码转换,假设/要求此法律为真?
这听起来不太可能,因为它Applicative甚至不是Monad(还)的超类.然而,这些"法则"允许代码的读者进行转换,这同样重要.
注意:如果你需要在Applicative实例中反转效果的顺序Control.Applicative.Backwards,就像Gabriel Gonzalez指出的那样.此外,(<**>)翻转参数但仍然从左到右排序效果,因此它也可用于反向排序.同样,(<*)不是flip (*>),因为左右两个序列效应.