カリー化とは
カリー化(currying)とは、複数の
引数を取る関数を、一つの
引数を持つ関数に変換するプロセスを指します。この技法は、特に関数型プログラミングの文脈で重要な概念であり、論理学者
ハスケル・カリーの名前に由来しています。実際には、カリー化の考案者としてはモーゼス・ショーンフィンケルや
ゴットロープ・フレーゲがクレジットされています。
カリー化の基本概念
カリー化を理解するためのシンプルな例として、関数 f(a, b) = c を考えます。この関数 f は二つの
引数 a と b を取り、結果 c を返します。カリー化された関数 F(a) は、最初の
引数 a を受け取り、その後 g(b) を呼び出すことで最終的な結果 c を返す関数となります。この場合、g は b を
引数として受け取り、最終的に c を生成するサブルーチンです。
一般的に、関数 f の形式は f: (X × Y) → Z と表現され、これをカリー化した関数 g は g: X → (Y → Z) という形になります。つまり、g は最初の
引数を受け取った後、次に二つ目の
引数を受け取る関数を返すわけです。
カリー化の応用
カリー化は計算機科学の理論において重要な役割を果たしています。複数の
引数を持つ関数を一つの
引数を持つ複数の関数に分離することで、単純になった理論的モデルでの研究が可能になります。また、
圏論ではカリー化の概念が
デカルト閉圏における冪対象の
普遍性と関連付けられています。この関連は特定の対象間の射(マップ)を一意に対応させることにより、新たな理論的洞察を提供します。
部分適用の容易さも、カリー化の重要な利点の一つです。たとえば、加算関数をカリー化して、最初の
引数に 1 を適用することで簡単にインクリメント関数を作成することが可能です。
カリー化を取り入れた
プログラミング言語もあります。特に、MLや
Haskellでは関数は常に一つの
引数を取る形式を持ち、複数の
引数を持つ関数は基本的にネストした一
引数関数の構文の
糖衣構文として扱われます。また、LISP、
Scheme、F#、
Scala、
Erlang、
Eiffel、
Perl、
Ruby、
Python、
R言語、
S言語、
JavaScript、Swiftなど、
第一級関数を扱える言語でもカリー化関数の作成が可能です。
カリー化の実装例
例えば、除算を行う関数 div(x, y) = x / y をカリー化した関数を cdiv(x) とすると、この cdiv 関数は
引数 x のみを受け取ります。その後、inv = cdiv(1) とすると、inv は y のみを
引数に取る関数となり、inv(y) = 1 / y という計算を行います。このように、カリー化を利用することで新たな関数を簡単に生成することができます。
カリー化と部分適用の違い
カリー化と部分適用は混同されがちですが、これらは異なる操作です。部分適用とは、複数の
引数を持つ関数に対して一部の
引数に具体的な値を適用する操作を指します。たとえば、先述の div(x, y) から inv(y) を生成する場合がこれにあたります。一方、カリー化は
引数への具体的な適用を行わず、代わりに新しい関数を生成することが主な目的です。カリー化が行う変換は、関数の形を変えるものであり、実
引数を適用する段階までは進まない点に注意が必要です。
タプル引数との関連
Haskellでは、標準関数 curry がカリー化とは厳密には異なる動作をすることもあります。この関数は、二つの要素からなるタプルを
引数に取る一
引数関数を、二つの
引数を持つ関数に変換します。
Haskellの全ての関数は本来一つの
引数を持つものとして扱われており、厳密にはカリー化を行う関数を定義することは難しいですが、理論的には二つの要素を持つタプルの型を直積とみなすことで、相互に理解できる部分があります。
まとめ
カリー化は、関数プログラミングにおいて極めて重要な概念であり、関数をより柔軟かつ使いやすくする手段を提供します。この技術を理解することで、効率的なプログラムを書くための新たな視点を得ることができるでしょう。