読者です 読者をやめる 読者になる 読者になる

kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

カリー化 != 部分適用

最近、ネット上でカリー化に関する記事を読んでいると、特にGroovy界隈でカリー化に関して誤解がまかり通っているようなので(特に実用的なGroovy: カレー化クロージャーによるファンクショナル・プログラミングはひどい。そもそも、Groovyの標準ライブラリ自体がカリー化を行うための関数ではないものにcurryとか付けてるから仕方無いのかもしれんが)、一言言っておく。

カリー化というのは、Groovyで言うと、

def add = {x, y -> x + y}

のように、xとyという複数の引数を取って値を返す関数を

def add = {x -> {y -> x + y}} //間違ってパースできないコードになっていたので修正(12/17)

のように、一つの引数xをとって、「yを引数にとって値を返す関数」を値として返すような関数に変換すること、あるいは最初からそのように表現することを言うのであって、

def add = {x, y -> x + y}
def add2 = add.curry(2) //部分適用であって、カリー化ではない!

のように、複数引数の関数に対して、一部の引数にだけ値を束縛した新しい関数を返すような操作(部分適用と呼ばれるもの)の事を指すのではない。

もちろん、カリー化された関数に対して、最初の引数に対してだけ値を束縛することで部分適用を達成することはできるわけだが、それはあくまでカリー化された関数があれば、部分適用(の内特殊な場合)が楽に記述できるというだけで、両者が同じであるということを意味しない。たとえば、Scalaでは_を使うことで、以下のように部分適用を簡単に記述できるようになっているが、この機能はカリー化とは無関係だ。

List(1, 2, 3).map(_ + 1)

ちなみに、Groovyでカリー化を行う関数を記述すると以下のようになる(2引数の場合):

def curry(f) {
  {x -> {y -> f(x, y) } }
}
def curriedAdd = curry({x, y -> x + y})
def add2 = curriedAdd(2)
println(add2(3))