如何设置隐式转换以允许数字类型之间的算术?
我想实现一个类 C
来存储各种数字类型以及布尔值的值。此外,我希望能够在类型之间操作此类的实例,并在必要时进行转换 Int -->双精度型和布尔型 -> 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
是菜单上的下一个......),它会变得很麻烦。
也许有人有另一种方法来实现这一切?感觉好像有一些简单的事情我忽略了。
提前致谢!
I'd like to implement a class C
to store values of various numeric types, as well as boolean. Furthermore, I'd like to be able to operate on instances of this class, between types, converting where necessary Int --> Double
and Boolean -> Int
, i.e., to be able to add Boolean + Boolean
, Int + Boolean
, Boolean + Int
, Int + Double
, Double + Double
etc., returning the smallest possible type (Int
or Double
) whenever possible.
So far I came up with this:
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
}
This works for some cases (1, 2, 3) but doesn't for (4, 5). The reason is that there is implicit widening of type from lower to higher, but not the other way. In a way, the method
def +[T <% A](that:C[T]) = s.add(this.n, that.n)
somehow needs to have a partner method that would look something like:
def +[T, A <% T](that:C[T]):T = that.s.add(this.n, that.n)
but that does not compile for two reasons, firstly that the compiler cannot convert this.n
to type T
(even though we specify view bound A <% T
), and, secondly, that even if it were able to convert this.n
, after type erasure the two +
methods become ambiguous.
Sorry this is so long. Any help would be much appreciated! Otherwise it seems I have to write out all the operations between all the types explicitly. And it would get hairy if I had to add extra types (Complex
is next on the menu...).
Maybe someone has another way to achieve all this altogether? Feels like there's something simple I'm overlooking.
Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好吧,丹尼尔!
我已将解决方案限制为忽略布尔值,并且仅适用于具有弱最小上界且具有 Numeric 实例的
AnyVals
。这些限制是任意的,您可以删除它们并对类型之间的弱一致性关系进行编码 -a2b
和a2c
的实现可以执行一些转换。考虑隐式参数如何模拟继承(传递类型(Derived => Base)或弱一致性的隐式参数是很有趣的。它们非常强大,特别是当类型推断器帮助您时。
首先,我们需要一个类型类来表示我们感兴趣的所有类型对
A
和B
的弱最小上界。方法
unify
返回类型C< /code>,它是由类型推断器根据隐式值的可用性计算出来的,作为隐式参数
ev
我们可以将其插入到您的包装类 C 中,如下所示,也需要一个
。 >Numeric[WeakLub]
这样我们就可以添加这些值最后,将它们放在一起:
Okay then, Daniel!
I've restricted the solution to ignore Boolean, and only work with
AnyVals
that have a weak Least Upper Bound that has an instance ofNumeric
. These restrictions are arbitrary, you could remove them and encode your own weak conformance relationship between types -- the implementation ofa2b
anda2c
could perform some conversion.Its interesting to consider how implicit parameters can simulate inheritance (passing implicit parameters of type (Derived => Base) or Weak Conformance. They are really powerful, especially when the type inferencer helps you out.
First, we need a type class to represent the Weak Least Upper Bound of all pairs of types
A
andB
that we are interested in.The method
unify
returns typeC
, which is figured out by the type inferencer based on availability of implicit values to provide as the implicit argumentev
.We can plug this into your wrapper class C as follows, also requiring a
Numeric[WeakLub]
so we can add the values.And finally, putting it all together:
有一种方法可以做到这一点,但我将其留给retronym 来解释一下,因为他写了这个解决方案。 :-)
There's a way to do that, but I'll leave it to retronym to explain it, since he wrote this solution. :-)