How to set up implicit conversion to allow arithmetic between numeric types?

前端 未结 2 1797
终归单人心
终归单人心 2020-12-09 06:07

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

相关标签:
2条回答
  • 2020-12-09 06:35

    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 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.

    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 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)
      }
    }
    

    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.

    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
    
    0 讨论(0)
  • 2020-12-09 06:48

    There's a way to do that, but I'll leave it to retronym to explain it, since he wrote this solution. :-)

    0 讨论(0)
提交回复
热议问题