可変引数はどのように実装できますか?引数リストの終わりを知らせる何らかのメカニズムが必要です。これは
- 特別なターミネーター値、または
- 追加パラメーターとして渡される可変引数リストの長さ。
これらのメカニズムは両方とも、可変引数を実装するためにカリー化のコンテキストで使用できますが、適切なタイピングが大きな問題になります。関数を扱っていると仮定しましょうsum: ...int -> int
この関数がカリーを使用除いて(したがってsum: int -> ... -> int -> int
、引数の数が分からないことを除いて、実際にはのような型があります)。
ケース:ターミネーター値:をend
特別なターミネーターT
とし、のタイプにしsum
ます。end
関数への適用がreturn:sum: end -> int
に適用され、intに適用されると、別のsum-like関数:が得られることがわかりましたsum: int -> T
。したがってT
、これらの型の結合は次のとおりT = (end -> int) | (int -> T)
です。代入することによりT
、我々は、以下のような様々な可能性のあるタイプを取得しend -> int
、int -> end -> int
、int -> int -> end -> int
などを、しかし、ほとんどのタイプのシステムは、このようなタイプに対応していません。
ケース:明示的な長さ:vararg関数の最初の引数は、可変引数の数です。ですからsum 0 : int
、sum 1 : int -> int
、sum 3 : int -> int -> int -> int
などこれは、いくつかのタイプのシステムでサポートされているとの一例です依存タイピング。実際には、引数の数は通常のパラメーターではなく型パラメーターになります。関数のアリティが実行時の値に依存することは意味がなく、s = ((sum (floor (rand 3))) 1) 2
明らかに不適切です。これは、、またはのいずれかs = ((sum 0) 1) 2 = (0 1) 2
に評価されます。s = ((sum 1) 1) 2 = 1 2
s = ((sum 2) 1) 2 = 3
実際には、これらの手法はエラーが発生しやすく、一般的な型システムでは(意味のある)型を持たないため、使用しないでください。代わりに、値のリストを1つのパラメーターとして渡しますsum: [int] -> int
。
はい。オブジェクトが関数と値の両方として表示される可能性があります。たとえば、強制のある型システムなどです。ましょうsum
a SumObj
、それは2つの強制を持っています:
coerce: SumObj -> int -> SumObj
許可する sum
関数として使用
coerce: SumObj -> int
結果を抽出できます。
技術的には、これが有する、上記ターミネーター値ケースの変形例でありT = SumObj
、そしてcoerce
タイプの非ラッパーです。多くのオブジェクト指向言語では、これはC ++などの演算子のオーバーロードで簡単に実装できます。
#include <iostream>
using namespace std;
class sum {
int value;
public:
explicit sum() : sum(0) {}
explicit sum(int x) : value(x) {}
sum operator()(int x) const { return sum(value + x); } // function call overload
operator int() const { return value; } // integer cast overload
};
int main() {
int zero = sum();
cout << "zero sum as int: " << zero << '\n';
int someSum = sum(1)(2)(4);
cout << "some sum as int: " << someSum << '\n';
}