Ответ 1
Хорошо, Даниэль!
Я ограничил решение игнорировать Boolean и работает только с AnyVals
, у которого есть слабая наименее верхняя граница, у которой есть экземпляр Numeric
. Эти ограничения произвольны, вы можете удалить их и закодировать свои собственные слабые отношения соответствия между типами - реализация a2b
и a2c
может выполнять некоторое преобразование.
Интересно рассмотреть, как неявные параметры могут имитировать наследование (передавая неявные параметры типа (Derived = > Base) или Weak Conformance. Они действительно мощные, особенно когда тип inferencer помогает вам.
Во-первых, нам нужен класс типа для представления наименьшей верхней верхней границы всех пар типов A
и B
, которые нас интересуют.
sealed trait WeakConformance[A <: AnyVal, B <: AnyVal, C] {
implicit def aToC(a: A): C
implicit def bToC(b: B): C
}
object WeakConformance {
implicit def SameSame[T <: AnyVal]: WeakConformance[T, T, T] = new WeakConformance[T, T, T] {
implicit def aToC(a: T): T = a
implicit def bToC(b: T): T = b
}
implicit def IntDouble: WeakConformance[Int, Double, Double] = new WeakConformance[Int, Double, Double] {
implicit def aToC(a: Int) = a
implicit def bToC(b: Double) = b
}
implicit def DoubleInt: WeakConformance[Double, Int, Double] = new WeakConformance[Double, Int, Double] {
implicit def aToC(a: Double) = a
implicit def bToC(b: Int) = b
}
// More instances go here!
def unify[A <: AnyVal, B <: AnyVal, C](a: A, b: B)(implicit ev: WeakConformance[A, B, C]): (C, C) = {
import ev._
(a: C, b: C)
}
}
Метод unify
возвращает тип C
, который вычисляется индексом типа на основе доступности неявных значений для предоставления в качестве неявного аргумента ev
.
Мы можем подключить это в ваш класс-оболочку C следующим образом, также требуя Numeric[WeakLub]
, чтобы мы могли добавить значения.
case class C[A <: AnyVal](val value:A) {
import WeakConformance.unify
def +[B <: AnyVal, WeakLub <: AnyVal](that:C[B])(implicit wc: WeakConformance[A, B, WeakLub], num: Numeric[WeakLub]): C[WeakLub] = {
val w = unify(value, that.value) match { case (x, y) => num.plus(x, y)};
new C[WeakLub](w)
}
}
И, наконец, все вместе:
object Test extends Application {
val n = new C[Int](10)
val d = new C[Double](10.5)
// The type ascriptions aren't necessary, they are just here to
// prove the static type is the Weak LUB of the two sides.
println(d + n: C[Double]) // C(20.5)
println(n + n: C[Int]) // C(20)
println(n + d: C[Double]) // C(20.5)
}
Test