[英]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.