Haskell 入門


便利な Applicative

Applicative とは

Applicative について少し調べてみたがよく分からなかった。ただし、Haskell には重要な概念のようで、自前の代数的データ型をモナドにするためには、そのデータ型は先ず fmap クラスと Applicative クラスのインスタンスでなければならない。しかし、pure, <$>, <*> などを例示に従って使ってみると結構便利なものだと感じた。これらは、Prelude 単体でも使える。理屈はどうでもいいのでとにかく使ってみた。

Applicative を使ってみる

Maybe モナドの Just 2 のコンテナの中身に (+10) を施したい時はよくあるだろう。モナドでこれを実現しようと思ったら次のようになる。

Prelude> Just 2 >>= \x -> return (x + 10)
Just 12

これを Applicative の演算子 <$> を使うと次のように書ける。

Prelude> (+10) <$> Just 2
Just 12

これは Haskell の $ 演算子の使い方とそっくりだ。

Prelude> (+10) $ 2
12

モナドのデータを使っているときは、これだけでも Applicative の演算子 <$> を使いたくなる理由になる。しかしこれに <*> 演算子を組み合わせるともっと便利だ。<*> を使えば、最初の関数が 2 引数関数でも計算できるのだ。

Prelude> (+) <$> Just 1 <*> Just 2
Just 3

これはモナドでないデータを Haskell で計算するときのやり方とそっくりだ。

Prelude> (+) 1 2
3

しかし、Haskell の $ 演算子を使って同じようなことをすると、次のように余分な括弧が必要だ。

Prelude> ((+) $ 1) $ 2
3

これをモナドで計算しようとすると次のように大変なことになる。

Prelude> Just 1 >>= \x -> (Just 2 >>= \y -> return (x + y))
Just 3

こうなってくるとモナドのコンテナの中身を計算するときは Applicative の演算子以外は使いたくなくなる。

次に pure 関数だが、これは引数の関数をモナドの値同士の関数に使えるように格上げするという働きがある。例えば、(+) 関数はモナドではない値の演算で次のように演算できるが、

Prelude> (+) 1 2
3

この演算子を Just 1 と Just 2 に使いたいときは、pure (+) を使えば良い。具体的には次のようになる。

Prelude> pure (+) <*> Just 1 <*> Just 2
Just 3

明らかに pure (+) <*> を使った演算と、(+) <$> の演算は同じ結果になる。

Prelude> (+) <$> Just 1 <*> Just 2
Just 3

Prelude で使える Applicative の演算子はこのほかにも *> と <* があるが、これは演算子の左右の値のどちらかを選択するという意味がある。

Prelude> Just 1 *> Just 2
Just 2
Prelude> Just 1 <* Just 2
Just 1

リストも Apllicative のインスタンスなので、次のような芸当もできる。

Prelude> (*2) <$> [1,2,3]
[2,4,6]

Applicative の概念はプログラム言語のパーサの開発から発生したものらしく、パーサーを記述するときにその効力がいかんなく発揮されるらしい。例えば、Parsec 3 には Applicative が採用されている。Applicative の便利さは Parsec を使って式の文法を記述しているときに特に感じる。

しかし、Applicative とは何かというような難しいことは置いておいて、モナドのコンテナの値を加工するのに便利な演算子があるという捉え方から、Applicative にアプローチして使い方に慣れていても良いような気がする。Monad 型のデータはすべて Applicative のインスタンスだからだ。

参考サイト

Applicative についてはブログ『あどけない話』の『Applicative のススメ』が分かりやすかった。