型クラスとimplicit parameter
型クラスの実装でイメージしているのはHaskell (GHC), Rust。
implicit parameterの実装でイメージしているのは一応Scalaだが、JVMからScalaまで丸ごとあまり詳しくない。
実行時のオーバーヘッド。いずれもコンパイラの戦略と実装に依る。
型クラスは基本的にはインスタンス・実装を静的に解決する
RustはそれをStatic dispatchするコードに展開する
RustにはTrait objectもある
GHCは辞書渡し + 最適化が頑張るだったような...
implicit parameterは (implicit parameterそれ自体は) ランタイムの他のパラメータと区別が無い (はず)
静的に決定しているのであれば最適化は勿論できる
コンテキスト情報。
Haskellはコンテキストがあっても無くても完全に明示的、 MultiParamTypeClasses でNullaryな型クラスも
Haskellでは基本モナドを使う
Rustのtraitは特定の型を Self として実装されるが、メソッドでそれを取る場合は明示的になる ( &mut self とか &self とか)
Scalaはimplicit parameterとして渡るオブジェクト自身 (this) が常に存在する
ScalaはHigher-kinded typesがあり、モナドがしばしば使われる
インスタンス/実装の重複。
型クラスはインスタンスがおおよそグローバルで、重複は基本eagerに弾く。マーカーとなる型を型クラスのパラメータに加えて明示的に渡すことは可能。
implicit parameterは解決時に決定できないエラーになる (はず)。explicitに渡して解決も可能。
関連型。RustやHaskellではインスタンスが一意なので、例えばRustで T: Iterator としたときに T::Item は T が実装する一意な Iterator インスタンスから一意に定まる (T に対して他のトレイト実装も要求していて、 type Item を持つトレイトが複数あって曖昧なときには <T as Iterator>::Item とする必要はある)。これによって、関連型を使うと複数の型パラメタ (+ fundeps) を使うよりも型パラメタを少なくできて楽、というのがパターンとしてある。
対してScalaでは、 Iterator[T] の実装は暗黙の引数として渡ってくるオブジェクトなので、単に [T](using it: Iterator[T]) とすると T::Item 相当の型は経路依存型? it.Item になる。この型を経路非依存に決定されるようにしたい場合は [T, I](using it: Iterator[T] { type Item = I }) と書くことができる (もうちょい楽な書き方無いかな...)。
/icons/hr.icon
おまけ、llrl (自作言語) では以下のようにした。 常にインスタンスを静的に解決する
常にStatic dispatch
やはり多くのケースはこれでカバーできる
セルフホスティング中、しばしばDynamic dispatchしたい箇所があった
この言語ではDynamic dispatchに相当する言語機能がクロージャしかないので、効率的な代替手段が無い
コンテキスト情報
インスタンスは静的なので必要なら常に型パラメタに加わる必要がある
関連型、fundepsあたりが無いので型推論がしばしば面倒くさい (単に未実装)
インスタンスの解決はインスタンスがimportされているかに基づいて行う
全てのインスタンスは名前を持つ
重複をケアする必要が無くなるが、インスタンスをimportするのが面倒くさい
explicitにインスタンスを指定する機能もあるべき (単に未実装)