overload で邪魔な type erasure をなんとかする

型レの話書くの忘れてた…まあいいか。

値を何かしらでラップするみたいなことは、まれによくあります。

trait Nanka {
  type Rep[T]
}

trait Add extends Nanka {
  def add(lhs: Rep[Int], rhs: Rep[Int])
  def add(lhs: Rep[Int], rhs: Rep[Double])
  ...
}

でまあこういうのはコンパイルエラーになる。add メソッドはどれも type erasure された結果、同じシグネチャを持つメソッドになってしまうからですね。全部 Java が悪い。まあ type erasure のおかげで higher order type param とか実現できたりもするしそれはそれで…いやそれはおいといて。

前こういうのに遭遇したときは、仕方がないので implicit parameter で type class 的に解決したのですが、それだと外部からの侵入に弱いとか、なんか書きづらい読みづらいとかでダサい。

そこで implicit parameter をもうちょっと違った使い方をする。

trait Add extends Nanka {
  class Dummy0
  class Dummy1
  implicit val dummy0 = new Dummy0
  implicit val dummy1 = new Dummy1

  def add(lhs: Rep[Int], rhs: Rep[Int])(implicit dummy: Dummy0)
  def add(lhs: Rep[Int], rhs: Rep[Double])(implicit dummy: Dummy1)
  ...
}

implicit parameter を追加すれば、シグネチャは別になるし、使う側は何もわからないし、書く側も少し煩雑だけれど、まあ type class ぽく書くよりはましだし、外部から侵入されたりもしない…
めでたしめでたし。

Lightweight Modular Staging のスライドで見たテクニックです。implicit object と path dependent type 使えないかなーとか思ったんですがダメでした。