カリー化に関する覚え書き
どこぞの某ウイルスのせいでメチャメチャ忙しい。 しかもここ1ヶ月くらいですっかり脅威扇動型ビジネス・モデルへと変貌したようで,ホンマにいい迷惑である。 もはやため息しか出ない。 ので,この件は無視することに決めた。
と,まぁ近況はこれくらいにして,今回は「カリー化」の話。 いや,関数型プログラミング言語への馴染みが薄いせいですぐ忘れちゃうのよ。
というわけで,覚え書きとして記しておく。
カリーは 飲みもの 動詞
Wikipedia によると「カリー化(currying)」とは
とある。 「カリー」は偉い数学者である Haskell B. Curry の名前から拝借したものらしい。 名前が動詞化しちゃってるのね(笑)
詳しくは近所の数学オタクに訊きなはれ。
関数型言語におけるカリー化
ガチの関数型プログラミング言語 Haskell ではカリー化は言語仕様に組み込まれていて,たとえば関数 add
の定義
add x y = x + y
は実際にはカリー化表現
add = \x -> \y -> x + y
の糖衣構文となっている1。
カリー化のメリットは関数の部分適用(partial application)が作れることで2,たとえば
increment = add 1
とすれば add
を実引数 1
で部分適用とした新しい関数 increment
をシンプルに記述できる。
もちろん,わざわざ名前を付けなくても無名関数として使えばいいのだが。
関数型じゃなくてもカリー化はできる
ガチの関数型プログラミング言語じゃなくても第一級関数(first-class function)をサポートするプログラミング言語であればカリー化の記述自体は可能である。
たとえば Go 言語なら
package main
import "fmt"
func add(x int) func(int) int {
return func(y int) int {
return x + y
}
}
func main() {
fmt.Println(add(1)(2)) //Output: 3
increment := add(1) //partial application
fmt.Println(increment(2)) //Output: 3
}
のように書ける。 JavaScript でも
function add(x) {
return function(y) {
return x + y;
};
}
console.log(add(1)(2)); //Output: 3
let increment = add(1); //partial application
console.log(increment(2)); //Output: 3
と書くことができる。
さらに JavaScript ではアロー関数式が使えるので,関数 add
の定義を
const add = x => y => x + y;
などと書くことも可能である。 ここまでくると,だいぶ関数型っぽいよね。
「それができる」ことと「そのように作られている」ことには天と地ほどの違いがある
この記事を書いて思い出したが,随分前に脊髄反射で
と呟いた。 今回の話はまさにそれ。
まぁ,そもそも Go 言語の場合はシンプルを旨とする思想な上に構文(statement)による制約が強いため,関数型っぽい記述には(書けるとしても)向いてない。
JavaScript は ES5 以降から関数型の要素を大幅に取り込んでいるが, Haskell と比較すれば分かるとおり,「関数」に対する考え方の根本が異なっている。
これは良し悪しの問題ではない。 まさに「制約は構造を生む」で,そうして生み出される構造と実装するシステムとの間で無理なくバランスし続けることがシステムを上手に運用するコツで,それこそが言語を選択する最重要ポイントだと思う(仕事ならね)。
システムを維持するために遺産や負債を抱え続けなければならない場合もあるが(それでも限度というか寿命はあるけど),そうでないならわざわざレガシーを選択する必然性は微塵もない。