前口上
HaskellにおけるFunctor、ApplicativeそしてMonadのつづき。
先日上記エントリを公開したところ、 このようなコメントをを頂いたので、自分なりに解釈してまとめてみた。@tezca686 氏にはこの場を借りてお礼申し上げます。
2つ以上の引数を持つ関数をどう解決するか
いや、わかってるCurryingされるからそもそもHaskellに複数の引数を持つ関数はないって 1。とはいえ前回同様、Curryingをあまり意識せず、複数の引数の関数ということで話を進めていく2。
普通のadd
では最初に、普通のadd関数を定義してみよう。概ね以下のようになる。
add :: (Num a) => a -> a -> a
add 10 20
30
ghciで動かした場合、概ねこんな感じになると思う。
Maybeで扱う
それではMaybeで扱いたい場合どうなるだろう?初手fmapを用いるだろうということはなんとなく理解できる3
tmp = fmap add (Just 42)
t: tmp
tmp :: Num a => Maybe (a -> a)
結果、Maybeと言うContextにWrapされたNum a=>a->aと言う関数が返ってくる。さて、この先ここに、Just 100を適用したいとしても残念ながらfmapで処理することはできない。なぜなら、どちらもLiftされた結果、MaybeにWrapされているからだ。
ここでApplicative
関数、値共に文脈f4にWrapされているばあい、そう、Applicativeを使うことで解決ができる。従って、以下のように書くことが可能だ。
fmap add (Just 42) <*> Just 100
Just 142
<$>を使う
さて、fmapも<*>と同様に、<$>という、二項演算子が定義されている.これを用いることで
add <$> Just 100 <*> Just 42
こんな感じでつなげても書ける。
ApplicativeDo拡張で書いてみる
さて、Applicativeも{-# LANGUAGE ApplicativeDo #-} を付与することで、Do記法で書くことが可能だ。ただし、MonadのDo記法とは異なり、前の状態を用いて値を定義できない琴に注意しなければならない。
{-# LANGUAGE ApplicativeDo #-}
add :: (Num a) => a -> a -> a
add x y = x + y
example :: Maybe Int
example = do
x <- Just 100 -- xは独立して定義可能じゃないとNG
y <- Just 42 -- yも同様に独立してなきゃNG
pure (add x y)
これでexampleはJust 142となる。
こんくるーじょん
前回、Applicativeだと何がうれしいのかいまいち理解できなかったけど、このようにfmapによって関数適用を受けた後の値は文脈fにWrapされることになる。そして、この文脈fがApplicativeを持っていなければ継続して値を適用して最終的な結果を得ることができなくなってしまう。
従って、Applicativeがあれば、fmapでLiftされた文脈に対して逐次敵に値を適用し、処理を進めることができることがうれしいという点を確認でき、理解することができた。
また、複数行にわたる場合、{-# LANGUAGE ApplicativeDo #-}を用いることで、あたかもDo記法の様に記載が可能だ。しかし、ApplicativeをMonadとして扱えるような魔法の記法ではないので、Applicativeの文脈にとどめておくのであれば、値同士に依存関係を持ち込んではならない点は注意しなければならない。