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化されているため、かなり重大な「観察されない」副作用を犠牲にしてはるかに効率的です。
いずれにせよ、それらが異なる場合は、それを文書化してください。