Вот идея:
class BiConverter[T, U, That](val toThat1: T => That, val toThat2: U => That)(implicit val num: Numeric[That])
trait LowPriorityBiConverterImplicits {
implicit def subtype[A: Numeric, T <: A, U <: A]: BiConverter[T, U, A] = new BiConverter[T, U, A](identity, identity)
}
object BiConverter extends LowPriorityBiConverterImplicits {
implicit def identityConverter[T: Numeric]: BiConverter[T, T, T] = new BiConverter[T, T, T](identity, identity)
implicit def firstAsSecond[T, U](implicit conv: T => U, num: Numeric[U]): BiConverter[T, U, U] = new BiConverter[T, U, U](conv, identity)
implicit def secondAsFirst[T, U](implicit conv: U => T, num: Numeric[T]): BiConverter[T, U, T] = new BiConverter[T, U, T](identity, conv)
}
class Arithmetic[T] private (A: Connector[T], B: Connector[T])(implicit n: Numeric[T]) {
import Numeric.Implicits._
val sum = new Connector(A.value + B.value)
}
object Arithmetic {
def apply[T, U, That](A: Connector[T], B: Connector[U])(implicit conv: BiConverter[T, U, That], tIsThatEvidence: T =:= That = null, uIsThatEvidence: U =:= That = null): Arithmetic[That] = {
val newA: Connector[That] =
if (tIsThatEvidence != null) A.asInstanceOf[Connector[That]]
else new Connector(conv.toThat1(A.value))
val newB: Connector[That] =
if (uIsThatEvidence != null) B.asInstanceOf[Connector[That]]
else new Connector(conv.toThat2(B.value))
new Arithmetic(newA, newB)(conv.num)
}
}
class Constant[T](var x: T) {
val value = new Connector(x)
}
class Connector[T](f: => T) {
def value: T = f
override def toString = value.toString()
}
Использование:
val n1 = new Constant(1)
val n5 = new Constant(5)
val a = Arithmetic(n1.value, n5.value)
val sum1 = a.sum.value // Int
println(sum1)
val n55 = new Constant(5.5)
val b = Arithmetic(n1.value, n55.value)
val sum2 = b.sum.value // Double
println(sum2)
val nBig5 = new Constant(BigInt(5))
val c = Arithmetic(n1.value, nBig5.value)
val sum3 = c.sum.value // BigInt
println(sum3)