こんにちは、かずやです。
Haskellに入門して早1週間ほど。今日は関数について学んでいたのですが、そこで出てきたカリー化という概念がちょっと面白かったので思考の整理がてらまとめていきます。
JavascriptとHaskellのコード例
カリー化というのは、複数の引数を取る関数の振る舞いに関する用語です。
例えばJavascriptのコード内に、
1 2 3 4 |
function add (x, y) { return x + y; } add(1,2) // -> 3 |
という関数があったとします。
この関数はxとyという2つの引数を取り、それを合計して返してくれます。非常に基礎的な関数です。
さて、同じ動作をHaskellで実装しようとすると、以下のようなコードになります。
1 2 |
add x y = x + y add 1 2 -- -> 3 |
- addは関数名
- x yは引数(Haskellでは引数にカッコをつけません。また複数の引数をとる場合はスペースで区切ります)
- イコールの右辺が実際の処理
となるので素直に読めば「xとyという2つの引数を取り、x+yという値を返す関数」と思うでしょう。
しかし、実際にはこの関数は少し違った動きをします。
カリー化とは「関数を返す」関数
言葉で説明する前に、Haskellの実際の動きをJavascriptで模倣してみたコードを例示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function add (x, y) { return function (y) { return x + y; } } add(1)(2) // -> 3 /* 1. add(1)により、 function (y) { return 1 + y; } という関数が返る 2. 1の関数に引数2を与え、最終的な値を得る */ |
Haskellにおける関数は、複数引数を持つ場合、基本的に「第一引数を元に関数を返す」という動作をします。
例の
1 |
add x y = x + y |
という関数の場合、1. まずxに値を代入してyのみを引数とする関数を作り、2. 1に第二引数を渡して値を取得する、というのが実際の処理となります。
何が嬉しいのか
この仕組みが用意されていることで、似たような関数を個別に定義する手間が省けます。
例えば「ある値に1,10,100を足す関数を作りたい」といった場合に、
1 2 3 |
add1 x = x + 1 add 10 x = x + 10 add 100 x = x + 100 |
とそれぞれに定義するのではなく、
1 2 3 4 |
add x y = x + y add1 = add 1 add10 = add 10 add100 = add 100 |
という形で共通化することができるのです。
へーすごいねー。
で、そうやって共通化できるのって何が嬉しいの?
上のコードだとむしろ行数増えてるんだけど?
と思ったあなた。
ぼくも今、同じことを思いました()
その辺、実用的な使い方に関してはもう少し使っていった上でわかってくるのかなと思っています。(オブジェクト指向でクラスとか初めて勉強した時もそうだったのではないかな)
というわけで、しばらく使ってみていい感じの実例が出てきたらまたアップデートしていく予定です。
おまけ
ちなみにHaskellの勉強には、この本使ってます。
章の行き来が多くて読むの大変だけど、網羅的なので初心者にはありがたいですね。まだおすすめって言えるほど読み込んでないですが、ハズレではないかと。
おまけ2
Haskellを学んでいると、様々な部分で「数学に出てくる思考を援用している」ことがわかります。
カリー化は言い方を変えると部分関数の生成ですし、関数の合成なんて考え方も出てきますし。そもそも前の記事で書いた「関数」という言葉の定義自体、数学的な定義ですよね。
個人的に面白かったのは、変数名に ‘ が使えることですね。数学でよくy = f'(x)みたいな表記をみますし、そういうの意識してるのでしょう。
再帰とかもよく出てきますが、数学的帰納法あたりが元になっている考え方な気がします。この辺の話は、また理解が深まってきた頃にきちんとまとめてみたいですね。
というわけで、今日はここまで。
ではではっ!