Scalaでは、オーバーロードと暗黙の引数解決の相互作用により、次のコードを使用可能にすることが不可能になっているようです。
trait Bijection[A, B] extends Function1[A, B] with Unapply[A, B] { self =>
def apply(a: A): B
def unapply(b: B): A
}
sealed trait Unapply[A, B] {
def unapply(b: B): A
}
object Bijection {
implicit def biject[A](a: A): Biject[A] = new Biject(a)
implicit object IntStringBijection extends Bijection[Int, String] {
override def apply(a: Int): String = a.toString
override def unapply(b: String): Int = b.toInt
}
}
sealed class Biject[A](a: A) {
def as[B](implicit f: Function1[A, B]): B = f(a)
def as[B](implicit f: Unapply[B, A]): B = f unapply a
}
ここでの目標は、Bijection [A、B]またはBijection [B、A]が暗黙のスコープで使用可能かどうかに関係なく、a.as[B]がタイプセーフな変換を実行することです。
これが機能しない理由は、コンパイラでのオーバーロードの曖昧性解消の後に暗黙の解決が行われるように見えるためです。また、「as」の両方の実装は同じ結果タイプであるため、コンパイラはそれを見つけようとさえしません。適切な暗黙が変換を実行できるスコープ内にあるかどうか。要するに、暗黙の解決は過負荷の曖昧性解消には使用されません。
'as'をオーバーロードしたい理由は、このライブラリのユーザーが呼び出しサイトで全単射の「方向」をエンコードする必要がないようにするためです。明らかに、Bijectを次のように実装できます。
sealed class Biject[A](a: A) {
def viaForward[B](implicit f: Function1[A, B]): B = f(a)
def viaReverse[B](implicit f: Unapply[B, A]): B = f unapply a
}
しかし、これは本質的にポン引きを不要にするため、本当に魅力的ではありません。明示的に全単射を通過させることもできますが、もちろん、使用される全単射をスコープに基づいて変化させることはできなくなります。
この問題に対する良い解決策はありますか?