Haskell は Scala よりもはるかに単純な言語であるため、Haskell の例から始めても問題ありません。Haskell では、すべての関数は数学的な意味での関数です。1 つの引数を取り、1 つの値を返します。Haskell にもタプルがあり、複数のパラメーターを受け取るように見える関数を、タプルから任意の関数として作成できます。例えば:
Prelude> let add = (\(x, y) -> x + y) :: (Int, Int) -> Int
Prelude> add (1, 2)
3
これで、この関数をカリーInt -> Int -> Int
化して、 の代わりにtype を持つ関数を取得できます(Int, Int) -> Int
。
Prelude> let curriedAdd = curry add
これにより、関数を部分的に適用できます。たとえば、次のようになります。
Prelude> let add3 = curriedAdd 3
Prelude> add3 1
4
したがって、カリー化のきれいな定義があります。これは、引数としてタプル (特にペア) を持つ関数を取り、ペアの最初の型を引数として取り、2 番目の型から関数を返す関数を返す関数です。ペアを元の戻り型に入力します。これは、これを言うための単なる言葉遣いです:
Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c
さて、次は Scala です。
Scala では、タプル引数を取る関数を使用することもできます。Scala には、複数の引数 (以上) を取る「関数」もありますFunction2
。これらは (紛らわしいことに) 異なる種類の動物です。
Scala には関数とは異なるメソッドもあります (ただし、これらはeta 展開によって多かれ少なかれ自動的に関数に変換できます)。メソッドには、複数のパラメーター、タプル パラメーター、または複数のパラメーター リストを含めることができます。
では、このコンテキストで何かをカリー化しているとはどういう意味でしょうか?
最も文字通り、カリー化は a Function2
(および up) を使用して行うものです。
scala> val add: Function2[Int, Int, Int] = (x: Int, y: Int) => x + y
add: (Int, Int) => Int = <function2>
scala> val curriedAdd = add.curried
curriedAdd: Int => (Int => Int) = <function1>
scala> val add3 = curriedAdd(3)
add3: Int => Int = <function1>
これは Haskell のケースで見たものとほぼ同じですが、Haskell には適切に存在しない複数の引数を持つ関数をカリー化している点が異なります。
これは、カレーという単語が実際に Scala 標準ライブラリに表示される唯一のコンテキストであると確信しています (コンパニオン オブジェクトuncurried
に付随するものは数えません)。ただし、Scala がメソッド、関数、など (誤解しないでください。私は Scala が大好きですが、言語のこの部分は完全な災害です)、次の文脈でもこの言葉を適用するのはかなり理にかなっているように思えます。Function
def add(x: Int, y: Int) = x + y
def curriedAdd(x: Int)(y: Int) = add(x, y)
ここでは、2 つのパラメーターを受け取るメソッドを、複数のパラメーター リストを持つメソッドに変更しました。<em>それぞれのパラメーターは 1 つのパラメーターのみを受け取ります (この最後の部分が重要です)。
実際、言語仕様もこの文脈でこの用語を使用しており、以下を「単一のカリー化された関数定義」と説明しています。
def func(x: Int)
(y: Int) = x + y
(もちろん、これは関数ではなくメソッドであるため、非常に混乱します。)
要約すると、複数のパラメーター リストは Scala でカリー化を実装する 1 つの方法ですが、複数のパラメーター リストを持つすべてのメソッドがカリー化されるわけではなく、各パラメーター リストが 1 つのパラメーターを持つメソッドのみがカリー化されます。とにかく、すべての用語はかなりどろどろしているので、正しく理解することについてあまり心配する必要はありません。