假设:
Applicative m, Monad m => mf :: m (a -> b), ma :: m a
似乎被视为一项法律:
mf <*> ma === do { f <- mf; a <- ma; return (f a) }
或更简洁:
(<*>) === ap
documentation for Control.Applicative
表示<*>
是“顺序申请”,这表明(<*>) = ap
。这意味着<*>
必须从左到右依次评估效果,以便与>>=
保持一致......但这感觉不对。 McBride and Paterson's original paper似乎意味着从左到右的排序是任意的:
IO monad,实际上任何Monad,都可以通过
pure
=来申请return
和<*>
=ap
。 我们也可以使用执行的ap
变体 相反顺序的计算,但我们将保持从左到右的顺序 在本文中。
因此,来自<*>
和>>=
的{{1}}有两个合法,非平凡的派生,具有不同的行为。在某些情况下,这两个派生中的都不合适。
例如,return
法律强制Data.Validation定义两种不同的数据类型:(<*>) === ap
和Validation
。前者具有类似于ExceptT的AccValidation
实例,以及具有有限效用的一致Monad
实例,因为它在第一个错误之后停止。另一方面,后者没有定义Applicative
实例,因此可以自由地实现Monad
,更有用的是积累错误。
StackOverflow上有一些discussion about this previously,但我认为它并没有真正解决问题:
有关仿函数,应用程序和monad的其他定律 - 例如同一性,相关性等 - 表达了这些结构的一些基本的数学属性。我们可以使用这些定律实现各种优化,并使用它们来证明我们自己的代码。相比之下,我觉得Applicative
法则强加了一种没有相应利益的任意约束。
对于它的价值,我宁愿放弃法律支持这样的事情:
(<*>) === 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
如何适应所有这些?注意:主要编辑以澄清问题的内容。 @duplode发布的答案引用了早期版本。
答案 0 :(得分:9)
嗯,我对目前给出的答案并不十分满意,但我认为附加的评论更具吸引力。所以我在这里总结一下:
我认为Functor
之后只有一个明智的Applicative
实例:
fmap f fa = pure f <*> fa
假设这是独一无二的,那么Functor
应该是Applicative
的超类,并且符合该定律。同样,我认为Functor
之后只有一个明智的Monad
实例:
fmap f fa = fa >>= return . f
同样,Functor
应该是Monad
的超类是有意义的。我曾经(并且,实际上,仍然有)的反对意见是Applicative
有两个合理的Monad
个实例,在某些特定情况下,甚至更多是合法的;为什么要一个人呢?
pigworker(original Applicative
paper上的第一作者)写道:
“当然不会跟随。这是一个选择。”
(twitter):“在单子中工作是一种不公正的惩罚;我们应该得到适用的注释”
duplode同样写道:
“...可以公平地说,
pure === return
和(<*>) === ap
不是强烈意义上的法律,例如monad法律就是这样......”“关于
LeftA
/RightA
提示:标准库中的其他地方也存在类似案例(例如Sum
中的Product
和Data.Monoid
。与Applicative
做同样的问题是,权重比关系太低而无法证明额外的精确度/灵活性。新类型会使应用风格的使用变得不那么令人愉快。“
所以,我很高兴看到这个选择被明确说明,通过简单的推理证明它使最常见的案例更容易。
答案 1 :(得分:7)
除此之外,您还问为什么Functor-Applicative-Monad
提案是一件好事。一个原因是因为缺乏统一意味着API有很多重复。考虑标准Control.Monad
模块。以下是该模块中基本上使用Monad
(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
个版本 - 但为什么需要首先学习所有重复?
答案 2 :(得分:5)
并且在我自己(也许是错误的)直觉中,给定
pure f <*> ma <*> mb
,不需要任何预定的排序,因为没有一个值相互依赖。
价值观并非如此,但效果确实如此。 (<*>) :: t (a -> b) -> t a -> t b
意味着您必须以某种方式组合参数的效果才能获得整体效果。组合是否可交换取决于实例的定义方式。例如,Maybe
的实例是可交换的,而默认情况下,&#34;交叉连接&#34;列表的实例不是。因此,在某些情况下,您无法强制执行某些订单。
Monad和Applicative有哪些法律(如果有的话)?
虽然公平地说,pure === return
和(<*>) === ap
(引用Control.Applicative
)并非强烈意义上的法律,例如monad法则是如此,它们有助于保持实例不足为奇。鉴于每个Monad
都会产生Applicative
的实例(实际上是两个实例,正如您所指出的那样),Applicative
的实际实例与Monad
的实际匹配很自然给我们。至于从左到右的约定,the order of ap
and liftM2
(在Applicative
被引入时已经存在,以及(>>=)
强加的顺序)是一个明智的决定。 (请注意,如果我们暂时忽略(>>=)
在实践中有多重要,那么相反的选择也是可辩护的,因为它会使(<*>)
和(=<<)
具有类似的类型,顺序效果的顺序相同。)
GHC或任何其他工具是否执行假设/要求此法律为真的代码转换?
由于Applicative
甚至不是Monad
(yet)的超类,这听起来不太可能。然而,这些法律和#34;允许代码的读者进行转换,这同样重要。
N.B。:如果你需要反转Applicative
实例中效果的排序,正如Gabriel Gonzalez指出的那样,有Control.Applicative.Backwards
。此外,(<**>)
翻转参数但仍然从左到右排序效果,因此它也可用于反向排序。同样,(<*)
不是flip (*>)
,因为两个序列效果都是从左到右。
答案 3 :(得分:1)
仅供记录,标题中问题的答案是:考虑
sequenceA :: Applicative f, Traversable t => t (f a) -> f (t a)
join :: Monad m => m (m a) -> m a
join . sequenceA
的类型是什么?
Monad m, Traversable m => m (m a) -> m a
Applicative m, Monad m, Traversable m => m (m a) -> m a
当然,join . sequenceA
是一种人为的情况,但肯定会出现需要 monad的情况,但您也希望使用Applicative
操作<*>
,*>
,<*
,<**>
等。然后:
Applicative
名称(恕我直言)比传统的monad操作更好。ap
,>>
,<<
等令人讨厌(&#34;哦,你不能在那里使用<*>
,那就是Monad
{1}}不是Applicative
&#34;;&#34;哦,你必须在<*>
使用Applicative
而不是Monad
}&#34;。)>>
和*>
执行不同的事情,那么您实际上无法使用Applicative
语法,因为它会做一些你不期望的事情。所以,实际上,每Applicative
Monad
与(<*>) = ap
兼容(在<?xml version="1.0" encoding="utf-8"?>
<Report xmlns="FPPD2.srdl" baseAmount_baseCreditAmount_TOTAL="1032.14" baseAmount_baseCreditAmount_COUNT="3" Today="2015-04-14T02:16:44" DocumentNumber=" 41" ExecDate="2015-04-14">
<Detail_2_1>
<Detail_2_1_Group_Collection>
<Item RowNumber="1" transactionDate="2004-04-01" accountCode_label_2="Account Code:" accountCode_3="PK181010" description_1="Telephone" paymentAccount_1="BANK" transactionReference="404TELE001NYC" description_label_2="Description:" description_2="Teleford communications" baseAmount_baseCreditAmount="428.57" baseAmount_baseDebitAmount_x="0.00" baseAmount_amount_1="428.57" baseAmount_baseDebitAmount="" baseAmount_amount_10="-428.57" supplierName_label_1="Supplier Name:" supplierName_1="Teleford & Communications" addressLine1_label_1="Address Line 1:" addressLine1_1="57 Harpermoorish Ave" addressLine2_label_1="Address Line 2:" addressLine2_1="" addressLine3_label_1="Address Line 3:" addressLine3_1="" addressLine4_label_1="Address Line 4:" addressLine4_1="Birmingham RG5 5BJ" addressLine5_label_1="Address Line 5:" addressLine5_1="UK" TownCity_label_1="Town/City:" TownCity_1="Birmingham" State_label_1="State:" State_1="" StateCode_label_1="State Code:" StateCode_1="" PostalCode_label_1="Postal Code:" PostalCode_1="RG5 5BJ" Country_label_1="Country:" Country_1="UK" document_1="Document:" TextBox_15="404TELE001NYC" bankAccountName_label_1="Bank Account Name:" bankAccountName_1="Travelbug Software Ltd" bankAccountNumber_label_1="Bank Account Number:" bankAccountNumber_1="DE21500500001234567897" BankSortCode_label_1="Bank Sort Code:" BankSortCode_1="BUINBGSF123" BankSubcode_label_1="Bank Subcode:" BankSubcode_1="" BankBranch_label_1="Bank Branch:" BankBranch_1="Southampton" BankDetailsCode_label_1="Bank Details Code:" BankDetailsCode_1="81010" BankName_label_1="Bank Name:" BankName_1="Midland Bank Plc" SwiftCode_label_1="Extension Swift Code:" SwiftCode_1="" />
<Item RowNumber="2" transactionDate="2004-05-01" accountCode_label_2="Account Code:" accountCode_3="PK181010" description_1="Telephone" paymentAccount_1="BANK" transactionReference="405TELE001NYC" description_label_2="Description:" description_2="Teleford communications" baseAmount_baseCreditAmount="428.57" baseAmount_baseDebitAmount_x="0.00" baseAmount_amount_1="857.14" baseAmount_baseDebitAmount="" baseAmount_amount_10="-857.14" supplierName_label_1="Supplier Name:" supplierName_1="Teleford & Communications" addressLine1_label_1="Address Line 1:" addressLine1_1="57 Harpermoorish Ave" addressLine2_label_1="Address Line 2:" addressLine2_1="" addressLine3_label_1="Address Line 3:" addressLine3_1="" addressLine4_label_1="Address Line 4:" addressLine4_1="Birmingham RG5 5BJ" addressLine5_label_1="Address Line 5:" addressLine5_1="UK" TownCity_label_1="Town/City:" TownCity_1="Birmingham" State_label_1="State:" State_1="" StateCode_label_1="State Code:" StateCode_1="" PostalCode_label_1="Postal Code:" PostalCode_1="RG5 5BJ" Country_label_1="Country:" Country_1="UK" document_1="Document:" TextBox_15="405TELE001NYC" bankAccountName_label_1="Bank Account Name:" bankAccountName_1="Travelbug Software Ltd" bankAccountNumber_label_1="Bank Account Number:" bankAccountNumber_1="DE21500500001234567897" BankSortCode_label_1="Bank Sort Code:" BankSortCode_1="BUINBGSF123" BankSubcode_label_1="Bank Subcode:" BankSubcode_1="" BankBranch_label_1="Bank Branch:" BankBranch_1="Southampton" BankDetailsCode_label_1="Bank Details Code:" BankDetailsCode_1="81010" BankName_label_1="Bank Name:" BankName_1="Midland Bank Plc" SwiftCode_label_1="Extension Swift Code:" SwiftCode_1="" />
<Item RowNumber="3" transactionDate="2004-02-01" accountCode_label_2="Account Code:" accountCode_3="PK181015" description_1="Leased Lines" paymentAccount_1="BANK" transactionReference="402TELE003TOK" description_label_2="Description:" description_2="Integration International" baseAmount_baseCreditAmount="175.00" baseAmount_baseDebitAmount_x="0.00" baseAmount_amount_1="1,032.14" baseAmount_baseDebitAmount="" baseAmount_amount_10="-1,032.14" supplierName_label_1="Supplier Name:" supplierName_1="Integration International" addressLine1_label_1="Address Line 1:" addressLine1_1="49, Station Road" addressLine2_label_1="Address Line 2:" addressLine2_1="" addressLine3_label_1="Address Line 3:" addressLine3_1="" addressLine4_label_1="Address Line 4:" addressLine4_1="London E4 7BJ" addressLine5_label_1="Address Line 5:" addressLine5_1="" TownCity_label_1="Town/City:" TownCity_1="London" State_label_1="State:" State_1="" StateCode_label_1="State Code:" StateCode_1="" PostalCode_label_1="Postal Code:" PostalCode_1="E4 7BJ" Country_label_1="Country:" Country_1="" document_1="Document:" TextBox_15="402TELE003TOK" bankAccountName_label_1="Bank Account Name:" bankAccountName_1="Creditor Account" bankAccountNumber_label_1="Bank Account Number:" bankAccountNumber_1="DE21500500009876543210" BankSortCode_label_1="Bank Sort Code:" BankSortCode_1="CRBABGSF" BankSubcode_label_1="Bank Subcode:" BankSubcode_1="" BankBranch_label_1="Bank Branch:" BankBranch_1="Cambridge" BankDetailsCode_label_1="Bank Details Code:" BankDetailsCode_1="81015" BankName_label_1="Bank Name:" BankName_1="Lloyds Bank" SwiftCode_label_1="Extension Swift Code:" SwiftCode_1="" />
</Detail_2_1_Group_Collection>
</Detail_2_1>
</Report>
意义上)是一个非常非常好的主意。