2

I want a class that encapsulates both integral and floating point numbers.

Basically I want to be able to do something like this

Const(5) + Const(6) = Const(11)
Const(5) + Const(6.0) =  Const(11.0)

The code with compiler errors.

case class Const[@specialized(Long,Double) T](value:T){

  def +(that:Const[T]) : Const[T] = {
    Const(this.value + that.value)
  }

}

----Error---

:11: error: type mismatch; found : T required: String Const(this.value + that.value)

(not sure why it is resolving to the string + operator)

smartnut007
  • 6,324
  • 6
  • 45
  • 52
  • 1
    it's resolving to the string because of implicit +, defined in Predef for String. So actually it means cannot find method "+" for T. – dk14 Dec 06 '14 at 10:57
  • 1
    You need to restrict `T` to `T:Numeric`. – Gábor Bakos Dec 06 '14 at 10:57
  • @GáborBakos Not sure where specifically i should add this restriction. Could you please post an edited version of the above. – smartnut007 Dec 06 '14 at 11:01
  • `case class Const[@specialized(Long,Double) T:Numeric](value:T){` – Gábor Bakos Dec 06 '14 at 11:02
  • @GáborBakos I had tried that and it didnt fix the issue. Thats why I suggested maybe you can paste the whole snippet. – smartnut007 Dec 06 '14 at 11:05
  • Have a look at this question: https://stackoverflow.com/questions/43382282/scala-generic-weighted-average-function It's a bit tedious, but you will be able to get the use case you are asking for. – Maths noob Sep 28 '17 at 11:51

2 Answers2

1

You want to be able to add instances of your Const class in the same way that Scala can add different numeric types. But Scala doesn't add different numeric types directly, it uses implicit conversions to first make them the same type. You can do this, too. First, you will need to add Numeric to your class, as Gábor notes:

case class Const[@specialized(Long, Double) T: Numeric](value: T) {
  import Numeric.Implicits._
  def +(that: Const[T]) = Const(value + that.value)
}

Then, define the implicit conversion:

implicit def constConvert[A, B: Numeric](a: Const[A])(implicit conv: A => B) =
  Const[B](a.value)

This accepts an implicit conversion, and thus generalizes the builtin implicit conversions to your type. Now you can write:

Const(5) + Const(6.0)  // Const(11.0)

which compiles to something like:

constConvert(Const(5))(_.toDouble) + Const(6.0)

Since Scala provides sensible implicit conversions for upcasting numeric types, and the constConvert method accepts such an implicit conversion, this will work generally for the builtin numeric types, plus any new types that define sensible conversions and Numerics.

However!

You should be aware that you won't really get the full benefits of specialization here, since the standard library Numeric is not not specialized (at the time of this writing), so there will still be autoboxing. You may instead want to use a specialized version of Numeric like the one provided by Spire.

Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45
0

Here is a working version (I think the type parameter is required for compatible usage when the two instances parametrized by different types):

  case class Const[@specialized(Long, Double) T: scala.Numeric](value:T){

    def +(that:Const[T]) : Const[T] = {

      //Const(implicitly[Numeric[T]].mkNumericOps(this.value) + that.value)
      import Numeric.Implicits._
      Const(this.value + that.value)

    }

  }

Usage: Const[Double](2) + Const(3.3)

Gábor Bakos
  • 8,982
  • 52
  • 35
  • 52
  • I see, but that kinda of defeats the purpose for me. As you might be able to guess, the purpose of this class is for dynamic execution and not any hardcoded constants where I know the types of all the values involved. – smartnut007 Dec 06 '14 at 11:34
  • Hmm. In that case probably you might define an additional type parameter to the `+` operator to allow widening. (Probably https://github.com/non/spire already done something like this.) – Gábor Bakos Dec 06 '14 at 11:38
  • I forget to add... For same type parameters, like in your first example this already works, only your second example requires additional type parameter. – Gábor Bakos Dec 06 '14 at 11:49
  • Yes, it works with both types are the same. Its because T is resolved to one type or the other. – smartnut007 Dec 06 '14 at 11:51
  • The problem is, when you have a `Const[Int]` for example, you cannot return also a `Const[Int]` when the other parameter is a `Const[Double]` (unless you round), so your signature `+(that:Const[T]) : Const[T]` is not correct for that usecase. – Gábor Bakos Dec 06 '14 at 12:00