はい、あなたが言ったように、最初は「2つの引数」を取り、2番目はタプルである1つの引数を取ります。ただし、実際に何が起こっているのかを理解するには、カリー化を理解する必要があります。ML では、すべての「関数」は 1 つの引数 (それ以上でもそれ以下でもありません) を取ります。
2 番目のケースでは、これは理解しやすく、タプルである 1 つの引数を取り、タプル内のもので何かを行い、結果を返します。最初のケースでは、1 つの引数を受け取り、別の引数を受け取り、結果を返す関数を返す関数を定義しています。
たとえば、これは 2 つint
の を取り、 を返す関数であるとしますint
。これら 2 つの関数の型を見ると、参考になります。2 番目の関数の型は です(int * int) -> int
。つまり、タプルから int への関数です。最初の関数の型は ですint -> int -> int
。これ->
は右結合であるため、 として解析できますint -> (int -> int)
。ご覧のとおり、int を取り、関数を返します。構文fun f x y = ...
は、より冗長な構文糖衣ですval f = fn x => fn y => ...
。この関数を適用すると、たとえばf 3 4
、関数の適用は左結合なので、実際にはであり(f 3) 4
、どのように機能するかを確認できます:f
を受け取り、int
別の int に適用される関数を返します。これがカレーの要点です。ML の構文は単純に実行を透過的にします。
カリー化バージョン(最初のバージョン)では、部分適用が可能です。つまり、関数は概念的には 2 つの引数を取りますが、その数の引数を与える必要はありません。では、f 3 4
上記の (これは です(f 3) 4
) を取得しf 3
、それを に適用する代わりに、4
それを保持して変数などに格納するとどうなるでしょうか? 「必要な数よりも少ない引数を関数に与える」ことで、残りの引数を取る関数を自動的に取得します。たとえば、 を実行すると、リスト内のfor eachmap (f 3) someList
のリストが計算されます。複雑な構文を記述する必要はありません。f 3 x
x
標準 ML では、ライブラリ関数はほとんどがカリー化されていない形式です。一方、OCaml と Haskell では、ほとんどがカリー化された形式です。(この質問を参照してください。) ただし、高階関数 (map や fold など) である SML ライブラリ関数は、関数の引数を別の (カリー化された) 引数で受け取る傾向があります。