DT日記

家を離れた自宅警備員の日記

F#がすげーかっこいい話(カリー化)

最近「実践F#」を教材に F# User Group - Japan の小泉さんのF#勉強会に参加してるのですが、F#かっけー!と思ったので紹介したいです。先週が1〜3章で、今週は4章が範囲。

たとへばF#ではこんなふうにコードを書くことができます。

> let f x y z = x + y + z ;;

val f : int -> int -> int -> int

> f 2 5 7 ;;
val it : int = 14

> ではじまってる行が対話環境に打ち込んだコードで、val ではじまってる行が結果を返す行、といふことでひとつ。

つまるところ、JavaScriptで見た目上に等価な書き方をすると、こんな感じ。Google ChromeJavaScriptコンソールですね。

>f = function(x,y,z){return x + y + z};
 function(x,y,z){return x + y + z}
>f(2,5,7);
 14

この程度ならJavaScriptがちょっと冗長ってだけで、同じことができてるのがわかりますね。

……と見せかけて、実は全然違ってる。

> let g = f 33 66 ;;

val g : (int -> int)

> g 1 ;;            
val it : int = 100

これは何かと見てみると、変数gに函数が入ってます*1。で、最後に 1 を渡してやると*2、みっつの数が足されて100が返ってくる!素敵!

一方でJavaScriptで引数をふたつしか渡さないなんてことをやると…

>g = f(33,66)
 NaN

NaN(Not a Number)と怒られるだけです。当たり前ですな。

実はさっきのコードを「見かけ」ではなくて「意味的」に書き直してみると、こんな感じ。

f = function(x){
	return function(y){
		return function(z){
			return x + y + z;
		}
	}
}

g = f(33)(66);

g(1);
f(33)(66)(99);

うん、長いし函数の呼び出し方が変っちゃってますね……。めんどい!

実はF#の函数に渡せる引数はひとつだけで、JavaScriptの例で示した様にF#で「複数の引数が渡された」ふうに見えた函数は、ひとつひとつが函数入れ子になってたのです。

JavaScriptでは「複数の引数をとる函数」と「複数の函数入れ子になった函数」では呼び出し方がまるで異なりますが、F#では空白区切りで値を並べるだけです。

こんな函数を「カリー化函数」と呼ぶらしいです。めちゃかっけーです。

僕はRubyKaigiからの帰りの飛行機でこれを読んで、すげーすげーかっけーすげー!と年甲斐もなくはしゃいでたのでした。ほんとの話。

非常に説明不足感がきついので、ぜひ自らF#に触れてみることをたいへんおすすめします。



RubyKaigiつながりでRubyの話をしておくと、実はRubyではこんなコードが書けます。

001:>> f = ->(x, y, z){ x + y + z }
=> #<Proc:0x000001010483f8@(irb):1 (lambda)>
002:>> g = f.curry[33][66]
=> #<Proc:0x00000101040338 (lambda)>
003:>> g[1]
=> 100

F#より冗長ですが、これはこれでかっけーです。

*1:函数型の世界では「函数に変数gが束縛される」ってふうに言ったりするけど、この場では割愛

*2:ほんとはこれも「函数に適用する」とか。