Haskellのid関数って何に使うんだろ…。

2 min read読了の目安(約2300字

Haskellの学習を進めていく中でid関数が出てきた際の疑問

「Haskellのid関数って何に使うんだろ...。」

を解消すべく調査しました。

id関数とは

ひとまずid関数の式の型を見てると下記となります。

Prelude> :t id
id :: a -> a

型変数aが使用されているため多相型で、idは多相関数であることがわります。
しかし、処理内容は引数を一つ受け取り、そのまま引数を返す処理になってます。
id関数に数値と文字列を渡してみると下記のようになります。

Prelude> id 1
1
Prelude> id "Hello World"
"Hello World"

最初見たときは何のために存在する関数なのかわかりませんでした。

id関数の存在意義

id関数に関して参照先のstackoverflowに下記の説明がありました。

It's useful as an argument to higher order functions (functions which take functions as arguments), where you want some particular value left unchanged.

訳すと「これは、高階の関数(関数を引数にとる関数)の引数として、ある特定の値を変更せずに残したい場合に便利です。」という意味になります。
つまり、高階関数に変更を加えずに関数を渡したい場合に使用できるという意味でしょうか。
この説明もわかりにくいかもしれませんが...。
下記でコードをもとに使い所を見ていきます。

id関数の使い所

■zipWith
zipWith関数を使用する際にid関数を用いてみます。
zipWith関数の式の型と使用例は下記の通りです。

Prelude> :t zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

zipWith関数の使用例は下記の通りで、2つの引数を受け取る関数とリストを2つ受け取り、関数をそれぞれの配列に対して順に適応させていって配列を作成しています。

Prelude> zipWith (+) [1, 2, 3] [3, 2, 1]
[4,4,4]

1つ目の配列[a]が引数を1つ受け取る関数の配列だった場合にid関数が使用できます。

Prelude> zipWith id [(+2), succ, id] [3, 2, 1]
[5,3,1]


■foldr
foldr関数を使用する際にid関数を用いてみます。
foldr関数の式の型は下記の通りです。

Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

foldr関数の使用例は下記の通りで、2つの引数を受け取る関数と初期値とリストを受け取り、関数をそれぞれの配列に対して右から順に適応させていって一つの値を返します。

Prelude> foldr (+) 0 [1,2,3]
6

1つ目の引数に.関数(関数合成を行う関数)を渡し、id関数と引数を1つ受け取る関数のリストを渡すことで部分適応された関数を作成できます。
部分適応されているので必要な最後の引数(ここでは7を渡している)を渡すことで演算結果が表示されます。

Prelude> :t foldr (.) id [(+2), (*2)]
foldr (.) id [(+2), (*2)] :: Num b => b -> b
Prelude> foldr (.) id [(+2), (*2)] 7
16

foldr関数とfoldl関数についてはまた別途、まとめたいと思います。

■Monoid
関数のモノイドのインスタンス宣言を行う際のmempty(単位元)として使用することができます。
memptyはx <> id =x(及びid <> x = x)であることを意味します。
なので整数の乗算では1、整数の加算では0になります。

instance Monoid (a -> a) where
        mempty        = id
        f `mappend` g = (f . g)

モノイドに関する詳しい説明はこちら
いずれはモノイドに関しても別途、まとめたいと思います。

参考