Я хотел бы реализовать класс 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
следующий в меню...).
Может, у кого-то есть еще один способ добиться всего этого? Чувствует, что там что-то простое, я не замечаю.
Спасибо заранее!