Я бы хотел реализовать класс C
для хранения значений различных числовых типов, а также логических значений. Кроме того, я хотел бы иметь возможность работать с экземплярами этого класса, между типами, преобразовывая при необходимости Int --> Double
и Boolean -> Int
, т. Е. Чтобы иметь возможность добавлять Boolean + Boolean
, Int + Boolean
, Boolean + Int
, Int + Double
, Double + Double
и т. Д., Возвращая наименьший возможный тип (Int
или Double
), когда это возможно.
Пока я придумал это:
abstract class SemiGroup[A] { def add(x:A, y:A):A }
class C[A] (val n:A) (implicit val s:SemiGroup[A]) {
def +[T <% A](that:C[T]) = s.add(this.n, that.n)
}
object Test extends Application {
implicit object IntSemiGroup extends SemiGroup[Int] {
def add(x: Int, y: Int):Int = x + y
}
implicit object DoubleSemiGroup extends SemiGroup[Double] {
def add(x: Double, y: Double):Double = x + y
}
implicit object BooleanSemiGroup extends SemiGroup[Boolean] {
def add(x: Boolean, y: Boolean):Boolean = true;
}
implicit def bool2int(b:Boolean):Int = if(b) 1 else 0
val n = new C[Int](10)
val d = new C[Double](10.5)
val b = new C[Boolean](true)
println(d + n) // [1]
println(n + n) // [2]
println(n + b) // [3]
// println(n + d) [4] XXX - no implicit conversion of Double to Int exists
// println(b + n) [5] XXX - no implicit conversion of Int to Boolean exists
}
Это работает для некоторых случаев (1, 2, 3), но не для (4, 5). Причина в том, что существует неявное расширение типа от низшего к высшему, но не наоборот. В некотором смысле, метод
def +[T <% A](that:C[T]) = s.add(this.n, that.n)
каким-то образом нужен метод партнера, который будет выглядеть примерно так:
def +[T, A <% T](that:C[T]):T = that.s.add(this.n, that.n)
, но это не компилируется по двум причинам: во-первых, компилятор не может преобразовать this.n
в тип T
(даже если мы указываем границы вида A <% T
), и, во-вторых, даже если он был способен конвертировать this.n
, после стирания типа два +
метода становятся неоднозначными.
Извините, это так долго. Любая помощь приветствуется! В противном случае кажется, что я должен явно записать все операции между всеми типами. И было бы сложно, если бы мне пришлось добавить дополнительные типы (Complex
следующий в меню ...).
Может быть, у кого-то есть другой способ достичь всего этого? Кажется, что-то простое я пропускаю.
Заранее спасибо!