Applicative勉強中:Applicativeとモナド
昨日はリストへの関数適用からApplicativeパターンを見つけるところまでやりました。でも、GoFもいうように、パターンといわれるからには、あちこちで似たような用例が見つからないとおかしいですよね...論文では、リストのほかにモナドも例として取り上げています。先日話したap関数がモナドでの<*>にあたります:
ap :: Monad m => m (a -> b) -> m a -> m b ap :: (Monad m) => m (a -> b) -> m a -> m b ap = liftM2 id liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
というのがapの定義でした。
そして、Applicativeパターンを発掘する対象はsequenceです。
sequence :: (Monad m) => [m a] -> m [a] sequence [ ] = return [ ] sequence (c : cs) = do x <- c xs <- sequence cs return (x : xs)
sequenceはモナドであるリストの各要素を評価して、その結果をリストに入れて返すわけですが、これをapを使うように書き換えるとすれば、apの第一パラメタとして渡されるべき関数は何になるか...といっても、sequenceのやっていることといえば、モナドの評価、再帰呼び出し、リストの生成だけですから、答えはリストの生成になりますね…ということで、sequenceをapを使って書き換えてみましょう。
sequence ::(Monad m) => [m a] -> m [a] sequence = return sequence (c : cs) = return (:) `ap` x `ap` sequence xs
モナド的なコードがおおむね`ap`の中に飲み込まれてしまっているのが面白いですね。これを、昨日のtransposeと比べてみると、
transpose :: [ [a] ]-> [ [a] ] transpose [ ] = repeat [ ] transpose (xs : xss) = repeat (:) ‘zapp‘ xs ‘zapp‘ transpose xss instance Applicative [] where pure x = repeat x f <*> a = f `zapp` a
と、かなり似ていることがわかりますね。違うところは丁度Applicativeのクラスメソッドに置き換えられるところです。
instance Applicative (Monad m) where pure x = return x f <*> a = f `ap` a
と、ここまで来て、Applicativeはどうやらコンテナの各要素に関数を適用するのに使えるものだということがわかってきますね…そういわれて思うのは、それってmapと何が違うの?というとこですね…mapの定義を振り返ってみると、
map :: (a -> b) -> [a] -> [b]
ですから、Applicativeのようにモナドでもリストでもクラスインスタンスの定義があれば使えるわけではなく、リストに限定されてますね...だからわざわざ
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
とか用意されてたりするわけですね…Hoogleで色々と探していると、コンテナ型依存のマップをコンテナ抽象化したfmapというのが見つかります。
class Functor f where
fmap :: (a -> b) -> f a -> f b
ということで、fmapは実はFunctorというクラスの唯一のクラスメソッドです。クラスを使って抽象化しているおかげで、リスト型というコンテナ制約かられています。この、fmapちょっと見た感じ、Applicativeの<*>コンビネータにとてもシグネチャが似ていますね...<*> :: f (a -> b) -> f a -> f b
第一関数がコンテナ方に入っているかいないかの違いです。だったら、簡単にApplicativeからFunctorへの変換ができそうですね…
class Aplicative f where pure :: a -> f a <*> :: f (a -> b) -> f a -> f b class Functor (Applicative f) where fmap :: (a -> b) -> f a -> f b fmap fn fa = pure fn <*> fa
ということで、Applicativeな型は必ずFunctorであるということがいえそうです。
functorはmapを一般化したものですから、つまり、mapを使っていたところでは必ずApplicativeが使えるということもいえそうですね...Functorの唯一のクラスメソッドであるfmapのシグネチャはApplicativeの<*>に対応しているとすると、pureはFunctorにはないApplicative独自のものであることがわかります。
FunctorではFunctor型のコンテナの各要素に関数を適用することしかできませんが、Applicativeではそのほかに、任意の型aを使ってApplicativeコンテナを生成することができるわけですね...つまり、mapに渡すデータを生成する能力がある...そしてそれはFunctorにはない…
つぎにモナドをApplicativeとして扱うことを考えて見ます。最初に出てきたapの定義を見ると、2つのパラメタを頭から順番に評価してそれから結果を生成しています。評価はdo文の中で起きているので、MaybeモナドをApplicativeとした場合は2つのパラメタのどちらかがNothingであれば処理が中断することがわかります…
任意のモナドに対してApplicativeのクラスインスタンス定義ができたことからもわかるように全てのモナドはApplicativeであるということがいえます。でも、その逆はどうやら違うようです。
全てのApplicativeがモナドかどうかを調べるには、Applicativeのクラスメソッドだけを使ってモナドのクラスメソッドをあらわせるかを試してみるてがあります。モナドは
class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a
なわけですが、a >> b = (a >>= \_ -> b)とできますので、>>=ができれば>>はいりません。ということで、やってみましょう…
instance Monad (Applicative f) where (>>=) :: f a -> (a -> f b) -> m b (>>=) fa f = ?????
れれれ、f aからaを取り出すのは一体どうしたらいいのでしょう?ということで、Applicativeは値を取り込むことができても取り出すこ操作はApplicativeのクラス定義内では触れていないので、bindが表現できません。つまり、Applicative全てはMonadであるということはできないということがいえます…
以上、モナドとApplicativeの関係、FunctorとApplicativeの関係の部分を見てみました。
論文ではさらに、ApplicativeとMonoidの関係、ApplicativeとArrowの関係Traversableクラスなどについて触れられているのですが、僕の今の段階では知らないものが多すぎて大変なので、論文のほうはこの辺で(ここまでだいたい半分弱でしょうか?)ひとまずお休みにしようかと思います。興味のある方、この先を読んで、僕にもわかるように説明してください…(笑)
ではでは…