Haskell 入門


fmap

Haskell でプログラムしていると map 関数をよく使う。map 関数はリストの要素を関数で加工するときに使う。例えば、リストの要素をそれぞれ2倍にしたいときは次のようにする。

Prelude> map (*2) [1..5]
[2,4,6,8,10]

fmap は、この map をリスト以外の型に適用できるように一般化したものだ。したがって、もちろんリスト型にも使える。fmap をリスト型に使うと、map と同じ結果になる。

Prelude> fmap (*2) [1..5]
[2,4,6,8,10]

しかし、fmap はリスト型以外にも Maybe 型にも使える。

Prelude> fmap (*2) (Just 2)
Just 4

IO a 型にも使える。

Prelude> fmap (*2) (return 2) >>= print 4

要するに、fmap は [a] や Just a や IO a などのデータ型のコンテナを開けずに中身を処理する関数だ。中身を開けずにというのは、一般には、コンテナの中の値を処理するには一旦 >>= 演算子やパターンマッチでコンテナの中の値を取り出す必要があるからだ。

たとえば、Maybe a 型のデータ型のコンテナの内容を処理するには次のように一旦コンテナの中の生のデータを取り出して、それを加工してから、再度コンテナのデータ型に再ラッピングするという操作が必要だ。

Prelude> do {x <- (Just 2); return (x * 2)}
Just 4

fmap 関数は、このように多相関数なので、fmap を関数適用するデータ型は、Functor クラスのインスタンスでないといけない。上に述べたリスト型や Maybe 型や IO 型は標準で Functor クラスのインスタンスだ。

自前のデータ型も Functor クラスのインスタンスとして宣言すれば fmap 関数を使う事ができる。例えば次のような二分木のデータ型 Tree a を宣言した場合、

Prelude> data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving Show

Tree を Functor クラスのインスタンスとして宣言することができる。

Prelude> instance Functor Tree where
Prelude|   fmap f (Leaf a) = Leaf (f a)
Prelude|   fmap f (Branch lt rt) = Branch (fmap f lt) (fmap f rt)
Prelude|

上の instance 宣言ではTree a ではなく、Tree にするので注意が必要だ。

ここで、適当な木構造のデータを作り example という名前にする。

Prelude> let example = Branch (Leaf 1) (Branch (Leaf 2) (Leaf 3))

すると、次のように fmap 関数で全ての Leaf のコンテナの中身を一斉に加工できる。

Prelude> fmap (*2) example
Branch (Leaf 2) (Branch (Leaf 4) (Leaf 6))

便利なものだ。チュートリアルレベルのプログラムには出てこないが、実際のプログラムには多用されるらしい。