Applicativeのこと少し
スライドに「Monadのような計算(の一部)を applicative style(要は普通の関数型言語の書き方)で書けるようにする」と書いていますが、ちょっと何を言っているのか分からないかもしれません。
pricesにくだものの名前と値段の連想リストが入っているとして、二つの果物を買うのに必要な値段は
buy2 fruit1 fruit2 = lookup fruit1 prices >>= \pri1 -> lookup fruit2 prices >>= \pri2 -> return pri1 + pri2
と書けますが、もしlookupの返り値がMaybeでなければ
buy2' fruit1 fruit2 = (lookup fruit1 prices) + (lookup fruit2 prices)
と書くところです。Maybeモナドの文脈で計算させたいだけなのに、余計なpri1とかpri2とかいう変数が出てきて、ちょっと気持ち悪いと思いませんか?
Applicativeスタイルで書くと以下のようになります。
buy2 fruit1 fruit2 = (+) <$> lookup fruit1 prices <*> lookup fruit2 prices
ただし、
(<*>) :: Applicative m => m (a -> b) -> m a -> m b (<$>) :: Applicative m => (a -> b) -> m a -> m b
です。
<$>
は、ある関数を、mの中の値に対して適用できるように「持ち上げる(liftする)」関数です。listに対するmapを一般化した概念ともいえます(ある関数を、listの中の値に対して適用できるように持ち上げる)。一方、<*>
は、既にmの中に入ったある関数を、mの中の別の値に対して適用してやる関数です。<*>
が定義されているのはApplicativeであるための一番重要な条件なので、「適用的(Applicative)」という名前もしっくり来ます。
Haskellでは二項演算子はただの関数で、演算子っぽい名前がついた関数は中置記法で書くことができるものの、(+)という風に括弧で括って使うと、普通の関数と全く同じように前置記法で適用できることに注意が必要です。また、二引数を受け取る関数は、実際にはカリー化されていて、「まず一引数を受け取って別の一引数を受け取る関数を返す」関数として表現されていることが重要です。
心眼で余計な演算子を消す(Lisperが括弧を消すようなもの)と、
buy2' fruit1 fruit2 = (+) (lookup fruit1 prices) (lookup fruit2 prices)
と読むことができます。
つまりは
buy2' fruit1 fruit2 = (lookup fruit1 prices) + (lookup fruit2 prices)
ですね。
読みにくいだけだと思うかもしれないですが、この例くらいなら慣れれば読みやすくなる、という感じですかね。Applicativeに限った話ではないですが、気持ち悪い見た目には現実問題として慣れるしかないものの、本来は言語とかライブラリとかを作った側の責任であって、読みづらいと感じる自分は無能だとか感じる必要はないと思います。
Monadよりも力が限定されている分、Applicativeには無駄に混みいった制御構造を作ってしまいにくいというメリットがあります。Applicativeすごいんだよという話はApplicative Programming with Effectsに載っています(読んでません)。
GADT
もうちょっとまともに書く予定
言語内DSLに使う
http://www.haskell.org/haskellwiki/GADT
関数をどうするか
- Stringと任意のExpにしちゃうと、型の情報が消えちゃう
- Haskellの関数にエンコードすると、evalがしにくくなったり、任意の関数が書けてしまったり、pretty printできなかったり
PHOAS
- pに対して多相的なExpしか許さないことで任意の関数を書かせない
- 引数は必ずEVarを使って導入される
- pのkindは*->*、要はpに入るのはパラメタライズされる前の型。で、EVarはp a型の値を受け取る。ppt関数の中でのpは、保持する値は実は変数を表示するために割り振るStringであるものの、形式的にどんな型でもパラメタライズされ得るようにしておけば、関数のpretty print可能。
- Coqでプログラム変換が型を保持することを示すのに便利