如何设置隐式转换以允许数字类型之间的算术? [英] How to set up implicit conversion to allow arithmetic between numeric types?

查看:51
本文介绍了如何设置隐式转换以允许数字类型之间的算术?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个 C 类来存储各种数字类型的值,以及布尔值.此外,我希望能够对此类的实例进行操作,在类型之间,在必要时进行转换 Int -->DoubleBoolean ->Int,即能够添加Boolean + Boolean, Int + Boolean, Boolean + Int, Int+ DoubleDouble + Double 等,尽可能返回最小的类型(IntDouble).

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
}

这适用于某些情况 (1, 2, 3) 但不适用于 (4, 5).原因是类型从较低到较高的隐式扩展,而不是相反.在某种程度上,方法

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)

但由于两个原因无法编译,首先编译器无法将 this.n 转换为类型 T(即使我们指定了视图绑定 A <% T),其次,即使它能够转换 this.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.

对不起,太长了.任何帮助将非常感激!否则,我似乎必须明确写出所有类型之间的所有操作.如果我必须添加额外的类型(Complex 是菜单中的下一个......),它会变得很麻烦.

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.

提前致谢!

推荐答案

好的,丹尼尔!

我将解决方案限制为忽略布尔值,并且仅适用于具有弱最小上限且具有 Numeric 实例的 AnyVals.这些限制是任意的,您可以删除它们并编码您自己的类型之间的弱一致性关系——a2ba2c 的实现可以执行一些转换.

I've restricted the solution to ignore Boolean, and only work with AnyVals that have a weak Least Upper Bound that has an instance of Numeric. These restrictions are arbitrary, you could remove them and encode your own weak conformance relationship between types -- the implementation of a2b and a2c could perform some conversion.

考虑隐式参数如何模拟继承(传递类型为 (Derived => Base) 或弱一致性的隐式参数)很有趣.它们非常强大,尤其是当类型推断器帮助您时.

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.

首先,我们需要一个类型类来表示我们感兴趣的所有类型 AB 的弱最小上界.

First, we need a type class to represent the Weak Least Upper Bound of all pairs of types A and B that we are interested in.

sealed trait WeakConformance[A <: AnyVal, B <: AnyVal, C] {
  implicit def aToC(a: A): C

  implicit def bToC(b: B): C
}

object WeakConformance {
  implicit def SameSame[T <: AnyVal]: WeakConformance[T, T, T] = new WeakConformance[T, T, T] {
    implicit def aToC(a: T): T = a

    implicit def bToC(b: T): T = b
  }

  implicit def IntDouble: WeakConformance[Int, Double, Double] = new WeakConformance[Int, Double, Double] {
    implicit def aToC(a: Int) = a

    implicit def bToC(b: Double) = b
  }

  implicit def DoubleInt: WeakConformance[Double, Int, Double] = new WeakConformance[Double, Int, Double] {
    implicit def aToC(a: Double) = a

    implicit def bToC(b: Int) = b
  }

  // More instances go here!


  def unify[A <: AnyVal, B <: AnyVal, C](a: A, b: B)(implicit ev: WeakConformance[A, B, C]): (C, C) = {
    import ev._
    (a: C, b: C)
  }
}

方法unify返回类型C,它是由类型推断器根据隐式值的可用性计算出来的,作为隐式参数ev.

The method unify returns type C, which is figured out by the type inferencer based on availability of implicit values to provide as the implicit argument ev.

我们可以将其插入到您的包装类 C 中,如下所示,还需要一个 Numeric[WeakLub] 以便我们可以添加值.

We can plug this into your wrapper class C as follows, also requiring a Numeric[WeakLub] so we can add the values.

case class C[A <: AnyVal](val value:A) {
  import WeakConformance.unify
  def +[B <: AnyVal, WeakLub <: AnyVal](that:C[B])(implicit wc: WeakConformance[A, B, WeakLub], num: Numeric[WeakLub]): C[WeakLub] = { 
    val w = unify(value, that.value) match { case (x, y) => num.plus(x, y)}; 
    new C[WeakLub](w)
  }
}

最后,把它们放在一起:

And finally, putting it all together:

object Test extends Application {
  val n = new C[Int](10)
  val d = new C[Double](10.5)

  // The type ascriptions aren't necessary, they are just here to 
  // prove the static type is the Weak LUB of the two sides.
  println(d + n: C[Double]) // C(20.5)
  println(n + n: C[Int])    // C(20)
  println(n + d: C[Double]) // C(20.5)
}

Test

这篇关于如何设置隐式转换以允许数字类型之间的算术?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆