水島さんの記事 Scala doesn't Need Generics! (or You can Encode Generics Using Abstract Type Members) についたがくぞーさんの引用リプライ「おー、これimplicit parameterの解決も上手く動くのかな? 」 について考えて見ましょうという話です。時間がないのでかなり適当に書く。
abstract type members に対して Context Bound を記述することはできません。これは随分前の Scala Advent Calendar 向けの記事で一度書きました。
で、書けないなら別の方法でエンコードする必要がある。ML 風に abstract (type) members に対して何からを要求するなら、まあ普通にメンバとして要求すればいい。すごく簡単。
abstract class HashSet[E : Hashable] {
...
}
上記のコードは以下のようにエンコードすればよい。水島さんの作法に従うと…
trait HashSet { self =>
type E
def `E is hashable`: Hashable[self.E]
...
}
new HashSet {
type E = Int
val `E is hashable` = implicitly[Hashable[Int]]
}
Functor(関手ではなく、ML の言語機能の方) のパラメタとして、具象的な型と、それに対する操作を貰うようなイメージですね。そうです、案外知られて無いけれど、ML 系のモジュールベースのプログラミングも、Scala では普通に出来ます。皆モナド~みたいになってますけど、できます。確か真面目に比較してる論文もあったはず…
まあクラスの型変数はこれでいいけれど、メソッドは違ってくる。
def max(implicit ord: Ordering[E]): E
うーん。
def max(ord: Ordering[E]): E
いやこれじゃ implicit 消えて明示的に型クラスのインスタンス渡してるだけじゃん、implicit parameter の意味ないじゃんとなる。そもそも最初の例だってわざわざ明示的にインスタンス渡すのはだるい。
ということで一枚噛ます。
trait HashSet { self =>
type E
val `E is hashable`: Hashable[Int]
class Max(implicit ord: Ordering[E]) {
def max: E = ???
}
}
trait HashSetCompanion { self =>
type E
class Apply(implicit ev: Hashable[self.E]) {
def apply(): HashSet = new HashSet {
type E = self.E
val `E is hashable` = ev
}
}
}
これでまあなんか下みたいに書ける様になる(コンパイル通してないので通らなかったらすいません。キモチだ)
val companion = new HashSetCompanion { E = Int }
val set = new companion.Apply apply
val max = new set.Max max
うんだるいですね却下。generics 使いましょう。
type member のよくないところは、プライマリコンストラクタのパラメタ宣言からは見えないことですかねえ。
追記
trait だけでできないの、と言われそうだなーと思ってたら言われなかったので追記しておくと、trait だけでは implicit parameter の解決はできません。色々考えたけどどうやっても無理そうというです。implicit parameter の解決を行うには、引数リストを持てるような、コンストラクタやメソッド等がどうしても必要になります。
なのでまあ class 使わずに下のように書いてもいいわけです。
trait HashSet { self =>
type E
val `E is hashable`: Hashable[Int]
def Max(implicit ord: Ordering[E]) = new {
def max: E = ???
}
}
でもこれだと本当に「なんで def 一枚噛ますの馬鹿なの?」みたいな例になっちゃうので class にしました。
implicit なんてルールに従ってディクショナリ引っ張ってるだけなんだから手で書けばいいんですよ手で(CanBuildFrom のディクショナリとか IDE か reflection かコンパイラの力借りないと分かりそうもないけど)