Haskell の id の使い方
第1回 スタートHaskell2 : ATND に参加させていただいたんですが、
そこで id なる関数を知りました。
(実は H 本に出ているんだけど、割とさらっと扱われてた。)
最初、これはどんな場面で使うのか分からなかったんですが。
色々ヒントをいただいたりしながら、自分なりに調べてみました。
(@tanakh さんありがとうございます。)
id の型と挙動
まず、 id の型は以下です。
Prelude> :t id id :: a -> a
とても単純で、「何かの型の値を受け取り、その値をそのまま返す」関数と読めます。
型は多相型で、引数も戻り値も同じ型です。そして、ここには一切の制限がありません。
実は、扱う型について一切の制限が無いというのは、逆を言えば非常に大きな制限で、
それぞれが「どんな型クラスのインスタンスなのか」すらも制限されていないため、
それを扱うことができる関数の中では、「ただそのまま返す」以外のことが出来ないということです。
つまり、この型で宣言される関数は、基本的には「そのまま返す」この id の挙動しかできないそうです。
確かに、例えば関数内でちょっとでも、値の「計算」や「比較」でも行えば、
途端に、引数が Num や Ord の型クラスに属している必要が出てくるため、
型に、型クラスの制約がつきます。
次にこれの使い道をいくつかあげます。
利用例 1 (zipWith)
まずは zipWith の例です。
zipWith の型は以下。
Prelude> :t zipWith zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
関数と二つの配列を引数にとり、関数を適応した新しい配列を返します。
Prelude> zipWith (+) [1,2,3] [4,5,6] [5,7,9]
で、適応したい関数を配列に持ち、二個目の配列の各要素に適応する場合、
id を用いて以下のように書けます。
Prelude> zipWith id [(+2), succ, negate] [5,6,7] [7,7,-7]
利用例 2 (foldl)
foldl は左からの畳み込みを行う関数です。
型は以下。
Prelude> :t foldl foldl :: (a -> b -> a) -> a -> [b] -> a
畳み込む場合は、アキュームレータの初期値が必要です。
以下は、整数配列を加算で畳込みたいので、初期値を 0 にしています。
Prelude> foldl (+) 0 [1,2,3] 6
foldl で、関数の配列を合成する畳み込みを考えたとき、
id を初期値として使うことができます。
Prelude> let calc = foldl (.) id [succ, negate, (+3)] Prelude> calc 10 -12