カリー化と部分適用

カリー化( Currying )と部分適用( Partial Application )がちがうということについて、すっきりわからない、という話があるようなので書いてみる

前提

元の関数は 2 引数とし、ある式の後に (...) を付けることで適用をあらわす( Python 風)とする。関数そのものについての表記(「f(x, y) = x + y」のような)と、関数適用の表記(「f(1, 2)」のような)は文脈によって判断してください
本稿では、演算の優先を表すためのカッコは視認性のため [ ] を使う(理解を助けるため冗長になっても積極的に使う)
高階関数というものに対する理解は既にあるものとする

カリー化

たとえば、ある関数 foo(x, y) があるとして、これを、

foo(a, b) = [curried_foo(a)](b)

となるような(高階)関数 curried_foo にするのがカリー化である
関数を引数として、カリー化する(高階)関数 curry(f) があるとすると

[[curry(foo)](a)](b) = foo(a, b)

となる

部分適用

複数の引数を取る関数と、それに与えるべき一部の引数から、一部の引数のみを適用したような(高階)関数を返すのが、部分適用である。部分適用は curry を使って以下のように実装できる

def part_apply(f, x)
  return [curry(f)](x)

(諸事情などでこちらが curry といった名前であることがしばしばある)

Further Reading

以下お約束の「ラムダを多用した説明」となる
part_apply の(別の)実装として、以下のようなものが考えられる(例によって対象は 2 引数関数のみとする)
構文は PythonJavaScript のちゃんぽんのようなものとして見てください

def part_apply(f, x)
  return function (y) { return f(x, y) }

しかしここで、もっと「頭の良い」part_apply を考えることができる
たとえば対象とする関数 f が以下のようなものであったとしよう

def zero_if_negative(x, y)
  return if x <= 0 then 0 else y

このとき、x の値に応じて

part_apply(zero_if_negative, -1)
↓
function (y) { return 0 }

part_apply(zero_if_negative, 1)
↓
function (y) { return y }

のように、それぞれどちらかの特化した関数を返すような part_apply もありうる(もちろんあらゆる関数 f に対して対応できる必要がある)
プログラマであれば、「それマクロと最適化でできるよ」ないし最近の話題を追ってる方であれば「リフレクションとかメタプログラミングでは」という反応をされると思う(し、実際同じものである)が、理論計算機科学的にはこれを部分評価(Partial Evaluation wikipedia:部分評価)という