学習の素材は、
すごいHaskellたのしく学ぼう!
以前、Functor < Applicative < Monad
という関係性があると書いたもののその根拠を問われると、その時点の知識では より柔軟に計算を行える、という理解しかなかった。今回学びを進めていくうちにもう少し具体的に表せることがわかった。
より柔軟に計算を行える?
より柔軟に計算を行えるというのは、Monad
型クラスは、Functor
や Applicative
を内包しており、さらに便利な関数も持っていてより実用的な対処ができる ということが言いたかった。ところが、Monad
はサブクラスなどの型制約を持っておらず、内包しているかどうかはぱっと見ではわからない状態だった。
調べてみると、これはMonad
が Haskell に導入されたのは Applicative
よりもずっと前だったという背景が理由にあるらしい。ではどのような考えから、 Functor < Applicative < Monad
と言えるのだろうか。
Functor < Applicative
これは、コードから明らかで、Functor
のサブクラスとして Applicative
があり、Applicative
は fmap
に加えて <*>
などのアプリカティブ・スタイルがあり、文脈をもった関数をアプリカティブ値に対して続けざまに適用できるので、より実用的である。
class Functor f => Applicative f where
-- | Lift a value.
pure :: a -> f a
-- | Sequential application.
(<*>) :: f (a -> b) -> f a -> f b
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
-- This is essentially the same as liftA2 (const id), but if the
-- Functor instance has an optimized (<$), we want to use that instead.
-- | Sequence actions, discarding the value of the second argument.
(<*) :: f a -> f b -> f a
(<*) = liftA2 const
Applicative < Monad
Monad
には、型クラス制約がないため、別の手段でfmap
や<*>
などと等価な関数を用意しなくてはならない。やはりその通りで、それぞれに対応した関数として、liftM
、 ap
があった。
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
ap :: Monad m => m (a -> b) -> m a -> m b
return :: Monad m => a -> m a
Monad
はこのほかにも、バインド(>>=)
を持っていて、文脈をもった値を相互作用させる力も持っている。つまり、Monad
はFunctor
であり Applicative
であり、プラスαである。
##Functor < Applicative < Monad
これで、Monad
は Applicative
よりも実用的だと、その実装からも説明できるようになった。