jwvh jwvh - 1 month ago 5x
Scala Question

type parameters and numeric widening

As we know, we can add (subtract/multiply/etc.) two numbers of different

types and the result will be the wider of the two types, regardless of their order.

33F + 9L // Float + Long == Float
33L + 9F // Long + Float == Float

This is because each of the 7
classes (
) has 7 different
methods (and
, etc), one for every
type that can be received as a passed parameter. [There's an extra
method for handling a
parameter, but that need not concern us here.]

Now consider the following:

implicit class PlusOrMinus[T: Numeric](a: T) {
import Numeric.Implicits._
def +-(b: T) = if (util.Random.nextBoolean) a+b else a-b

This works if the two operands are the same type, but it also works if the type of the 1st operand is wider than the type of the 2nd.

11F +- 2L // result: Float = 9.0 or 13.0

I believe what's happening here is that the compiler uses weak conformance to achieve numeric widening on the 2nd operand (the
parameter) as it is passed to the

But the 1st operand won't be widened to match the 2nd. It won't even compile.

11L +- 2F // Error: type mismatch; found: Float(2.0) required: Long

Is there any way around this limitatiion?

I can't use a different type parameter for the
argument (
def +-[U: Numeric](b: U) = ...
) because a
, expressed via a type parameter, can only add/subtract it's own type.

Is the only solution to create 7 different classes (
etc.) with 7 different methods (
def +-(b:Short)
def +-(b:Int)
def +-(b:Long)
, etc.)?


Here is a way:

implicit class PlusOrMinus[T: Numeric](a: T) {
  import Numeric.Implicits._
  def +-(b: T) = plusOrMinus(a,b)
  def +-[U: Numeric](b: U)(implicit ev: T => U) = plusOrMinus[U](a,b)

  private def plusOrMinus[W: Numeric](a: W, b: W): W =
    if (util.Random.nextBoolean) a+b else a-b

Then, with this, I get the following interaction:

scala> 11F +- 2L
res0: Float = 9.0

scala> 11L +- 2F
res1: Float = 9.0

The idea is that if I could just have a function plusOrMinus, this whole problem would be trivial, since the same widening could happen for either arguments. After defining such a function, the problem becomes how to embed it into an implicit class to use it in an infix form.

Here, we have only two cases: either the second argument needs to be widened or the argument wrapped by the implicit class needs to be widened. The first of these cases is covered by the first +- method (for the reasons you observed above). For the second, however, we need to explicitly say that there is some conversion that is possible and pass the generic type of the conversion to plusOrMinus.