Functor
私は、インスタンスを最初に書いて見る傾向があります。LANGUAGE DeriveFunctor
プラグマを使用すると、data Foo a = Foo a deriving ( Functor )
ほとんどの場合に機能するため、二重にそうです。
Applicative
注意が必要なのは、あなたがあなたよりも一般的である場合のインスタンスの合意に関するものMonad
です。たとえば、これはErr
データ型です
data Err e a = Err [e] | Ok a deriving ( Functor )
instance Applicative (Err e) where
pure = Ok
Err es <*> Err es' = Err (es ++ es')
Err es <*> _ = Err es
_ <*> Err es = Err es
Ok f <*> Ok x = Ok (f x)
instance Monad (Err e) where
return = pure
Err es >>= _ = Err es
Ok a >>= f = f a
上記でインスタンスを順番に定義しましたが、Functor
個別に見てみるMonad
と、各インスタンスは正しいものです。残念ながら、Applicative
とMonad
インスタンスは一致しません:ap
とは、とのように明らかに(<*>)
異なります。(>>)
(*>)
Err "hi" <*> Err "bye" == Err "hibye"
Err "hi" `ap` Err "bye" == Err "hi"
感性のために、特に Applicative/Monad Proposal が全員の手に渡ると、これらは一致するはずです。定義した場合instance Applicative (Err e) where { pure = return; (<*>) = ap }
、それらは整列します。
Applicative
しかし、最終的には、との違いを慎重に分解してMonad
、それらが無害な方法で異なる動作をするようにすることができるかもしれませんApplicative
。これは実際にはかなり頻繁に発生しており、「無害」とは何を意味し、どのような種類の「観察」の下でインスタンスを調整する必要があるかについて、陪審はまだ少し外れていると感じています。おそらく、これが最も多く使用されているのは Facebook の Haxl プロジェクトです。このプロジェクトでは、Applicative
インスタンスがインスタンスよりも並列Monad
化されているため、かなり重大な「観察されない」副作用を犠牲にしてはるかに効率的です。
いずれにせよ、それらが異なる場合は、それを文書化してください。