多相関数

多相関数はどちらかといえば関数テンプレートに似ています。 けれども、前者は ATS における第一級の値ですが後者は違います。 例として次に定義する関数 swap_boxed は多相的です:

fun swap_boxed{a,b:type} (xy: (a, b)): (b, a) = (xy.1, xy.0)

型変数 ab はしばしば静的引数と呼ばれ、 xy は動的引数と呼ばれます。 例えば、次のコードは多相関数 swap_boxed を使っています:

val AB = (box("A"), box("B")) val BA1 = swap_boxed{boxstr,boxstr} (AB) val BA2 = swap_boxed (AB) // omitting type arguments may be fine

このとき boxstr は明示的にボックス化された string で、boxed(string) のように定義されます。 内部的には stringboxed(string) の間に違いはありません。 もし swap_boxed がなんらかの型 T1 と T2 に関する型 (T1, T2) のペアに呼び出された場合、T1 と T2 の両方がボックス化されていることが要求されます。 さもなければ、型エラーになります。 例えば、swap_boxed(0, 1) に呼び出すと、型 int がボックス化されていないという型エラーが発生します。 box(0) のようなボックス化された整数を作ろうとするかもしれませんが、これは型エラーになります。 ATS における型 int の整数値のサイズについて、何も前提がないからです。

多相関数を呼び出す時、静的引数を省略してコンパイラによって推論されることをしばしば期待します。 けれども推論に失敗したり、推論がプログラマが意図していたものとは違っているために、明示的に静的引数を指定することが必要になることも珍しくはありません。

次の多相関数の実装スタイルのように、静的引数を連続して渡すことも可能です:

// fun swap2_boxed{a:type}{b:type} (xy: (a, b)): (b, a) = (xy.1, xy.0) // val AB = (box("A"), box("B")) val BA1 = swap2_boxed (AB) // both static arguments to be synthesized val BA2 = swap2_boxed{...} (AB) // both static arguments to be synthesized val BA3 = swap2_boxed{..}{boxstr} (AB) // 1st static argument to be synthesized val BA4 = swap2_boxed{boxstr}{..} (AB) // 2nd static argument to be synthesized val BA5 = swap2_boxed{..}{..} (AB) // both static arguments to be synthesized val BA6 = swap2_boxed{boxstr}{boxstr} (AB) // both static arguments are provided //

特殊構文 {..} はこの適用に含まれる静的引数が推論されるべきであることを型検査器に指示します。 さらに特殊構文 {...} は残りの静的引数全てが推論されるべきであることを型検査器に指示します。

実際の場面で非常に一般的な多相関数に由来する2種類のエラーを見たことがあります:

混乱する可能性があるのに、なぜ関数テンプレートと多相関数が必要になるのでしょうか? 関数テンプレートだけを使い、多相関数を使うべきではない、というのがもちろん現時点ではもっともらしく思えます。 けれども、多相関数は依存型に強く結びついています。 多相関数のためのテンプレートである多相関数テンプレートに、実際には数多く出会うことになるでしょう。