学習の素材は、
すごい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 よりも実用的だと、その実装からも説明できるようになった。