型消去
型消去とは、型引数に_
を指定し、その情報をあえて捨てることです。型消去は多相型を持つ言語の多くが併せて持つ機能ですが、Ergの文法に即して言えば型引数消去といった方が正確でしょう。
もっともよく見られる型消去された型の例は[T, _]
でしょう。配列はコンパイル時にその長さが分からない場合もあります。例えば、コマンドライン引数を指すsys.argv
は[Str, _]
型です。コマンドライン引数の長さをErgのコンパイラは知りようがないため、長さに関する情報は諦めなくてはならないのです。
しかし、型消去された型は、されていない型のスーパータイプになる(e.g. [T; N] < [T; _]
)ため、より多くのオブジェクトを受け取れるようになります。
[T; N]
型のオブジェクトはもちろん[T; _]
型のメソッドを使用できますが、使用後n
の情報は消去されます。長さが変わってしまっているかもしれないからです。長さが変わらないならばシグネチャで示さなくてはなりません。
# 配列の長さが変わらないことが保証される関数(sortなど)
f: [T; N] -> [T; N]
# 長さが保障されない関数(filterなど)
g: [T; n] -> [T; _]
型指定自体で_
を使うとその型はObject
までアップキャストされます。
型でない型引数(Int, Bool型など)の場合、_
としたパラメータは未定義になります。
i: _ # i: Object
[_; _] == [Object; _] == Array
型消去は型指定の省略とは違います。一度型引数情報を消去してしまうと、再びアサーションしなければ情報は戻りません。
implicit = (1..5).iter().map(i -> i * 2).to_arr()
explicit = (1..5).iter().map(i -> i * 2).into(Array(Nat))
Rustでは以下のコードに対応します。
#![allow(unused)] fn main() { let partial = (1..6).iter().map(|i| i * 2).collect::<Vec<_>>(); }
Ergでは型の部分省略はできず、代わりに高階カインド多相を使用します。
# collectはカインドを受け取る高階カインドのメソッド
hk = (1..5).iter().map(i -> i * 2).collect(Array)
hk: Array(Int)