主な問題は数値型の変換だと思います。それでは、それをエンコードしましょう:
trait NumericConversion[X, Y] {
def convert(x: X): Y
}
もちろん、その抽象的な概念を指定する必要があります: (たとえば)
implicit object Int2IntNumericConversion extends NumericConversion[Int, Int] {
def convert(i: Int): Int = i
}
implicit object Double2DoubleNumericConversion extends NumericConversion[Double, Double] {
def convert(d: Double): Double = d
}
implicit object Int2DoubleNumericConversion extends NumericConversion[Int, Double] {
def convert(i: Int): Double = i.toDouble
}
比較方法は次のようになります。
def compareTwoNumbers1[N1, N2, N3](n1: N1, n2: N2)
(implicit conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
使用法:
compareTwoNumbers1[Int, Double, Double](3, 8D) // -1
残念なことに、型パラメーターを明示的に指定する必要があるため、次のことを試しました。
def compareTwoNumbers2[N3] = new {
def apply[N1, N2](n1: N1, n2: N2)(implicit conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
}
これは、1 つの型引数に縮小されます。
compareTwoNumbers2[Double](3, 8D) // -1
満足できないので、これを試しました:
trait NumericUpperBound[Num1, Num2, UpperBound]
implicit object NumericUpperBoundIDD extends NumericUpperBound[Int, Double, Double]
implicit object NumericUpperBoundDID extends NumericUpperBound[Double, Int, Double]
新しい比較方法:
def compareTwoNumbers3[N1, N2, N3](n1: N1, n2: N2)
(implicit nub: NumericUpperBound[N1, N2, N3],
conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
今では動作します:
compareTwoNumbers3(3, 8D) // -1
もちろん、すべてのプリミティブの型クラスを作成する必要があります。BigInt
ただし、後で などに拡張することは柔軟です。
編集
@wvxvw によるコメントは、マトリックスをNumericUpperBounds
回避するように私にインスピレーションを与えました。Byte
Short
trait ==>[X, Y] extends (X => Y)
object ==> {
def apply[X, Y](f: X => Y): X ==> Y = {
new (X ==> Y) {
def apply(x: X): Y = f(x)
}
}
}
implicit val Int2LongNumericConversion = ==> { x: Int => x.toLong }
implicit val Int2FloatNumericConversion = ==> { x: Int => x.toFloat }
implicit val Int2DoubleNumericConversion = ==> { x: Int => x.toDouble }
implicit val Long2FloatNumericConversion = ==> { x: Long => x.toFloat }
implicit val Long2DoubleNumericConversion = ==> { x: Long => x.toDouble }
implicit val Float2DoubleNumericConversion = ==> { x: Float => x.toDouble }
implicit def reflexiveNumericConversion[X]: X ==> X = new (X ==> X) { def apply(x: X): X = x }
trait NumericUpperBound[Num1, Num2, UpperBound]
implicit def reflexiveNumericUpperBound[X]: NumericUpperBound[X, X, X] = new NumericUpperBound[X, X, X] {}
implicit def inductiveNumericUpperBound1[X, Y](implicit ev: X ==> Y): NumericUpperBound[Y, X, Y] = new NumericUpperBound[Y, X, Y] {}
implicit def inductiveNumericUpperBound2[X, Y](implicit ev: X ==> Y): NumericUpperBound[X, Y, Y] = new NumericUpperBound[X, Y, Y] {}
def compareTwoNumbers[N1, N2, N3](n1: N1, n2: N2)
(implicit nub: NumericUpperBound[N1, N2, N3],
conv1: N1 ==> N3,
conv2: N2 ==> N3,
ord: Ordering[N3]): Int = {
ord compare (n1, n2)
}
compareTwoNumbers(9L, 13) // -1