由于Functor-Applicative-Monad Proposal,Monads 是 Applicative 的子類,而 Applicative 又是 Functor 的子類。從數學上講,這似乎是一個明智的選擇,我對此沒有任何問題。
然而,讓我惱火的是,即使對應的規則fmap和pure和<*>由 monad 定律固定,也需要寫下 Functor 和 Applicative 實體。事實上,在上面鏈接的提案中,他們自己寫道:“您可以通過添加以下代碼簡單地從 Monad 派生這些實體”:
import Control.Applicative (Applicative(..))
import Control.Monad (liftM, ap)
instance Functor m where
fmap = liftM
instance Applicative m where
pure = {- move the definition of `return` from the `Monad` instance here -}
(<*>) = ap
在大多數教程中,您只會看到相等pure=return,然后人們會return像以前一樣繼續在Monad 實體中進行定義。因此,實際上,為只想定義 Monad 實體的每個人添加了 10 行樣板代碼。
在數學中,人們通常也不會以這種方式進行運算。例如,在定義某個環的合成律時,通常不會再次明確提醒讀者環是加法下阿貝爾群的特例,而阿貝爾群又是群的特例。無論如何,這在定義上是清楚的。
因此我的問題是:盡管 Monads 是 Applicative 的子類,而 Applicative 又是 Functor 的子類,但在編譯器插入樣板代碼而不是程式員?
uj5u.com熱心網友回復:
檔案中的建議適用于過去Monad定義了實體但沒有定義的遺留型別Applicative。然后根據現有實體定義這些實體Monad是一種快速可靠的修復方法,可以讓包再次編譯。
但是,這不是新代碼應該采用的方式,因為Monad實際上是更高級的類。推薦的方法是
派生的
Functor實體。始終只有一種可能的方法可以做到這一點,編譯器可以為您做到。{-# LANGUAGE DeriveFunctor #-} data YourMonadType a = ... deriving (Functor)定義
Applicative。這不是完全自動的,有時<*>可能有點違反直覺,但您會掌握它的竅門,直接實作實際上比(<*>) = ap.instance Applicative YourMonadType where pure = {- direct definition here -} q <*> r = {- direct definition here -}定義
Monad。這根本不需要return(因為它是默認的= pure),只需要>>=.instance Monad YourMonadType where q >>= f = {- direct definition here -}旁注:可以說 的方法
Monad實際上應該join代替>>=。雖然return并>>=產生了所有Functor和Applicative方法,但join對它們來說是一個“正交特征”。
在現代 GHC 中,還可以Applicative通過via在WrappedMonadnewtype上使用策略來獲得派生實體:
{-# LANGUAGE DerivingVia, DeriveFunctor #-}
import Control.Applicative
data YourMonadType a = YourMonadType a a
deriving stock (Functor, Show)
deriving Applicative via (WrappedMonad YourMonadType)
instance Monad YourMonadType where
return x = YourMonadType x x
YourMonadType x y >>= f = YourMonadType fx fy
where YourMonadType fx _ = f x
YourMonadType _ fy = f y
ghci> YourMonadType ( 1) (*2) <*> YourMonadType 10 20
YourMonadType 11 40
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/387034.html
