jwvh - 1 year ago 89
Scala Question

# type parameters and numeric widening

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

`Numeric`
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
`Numeric`
classes (
`Byte`
,
`Short`
,
`Char`
,
`Int`
,
`Long`
,
`Float`
,
`Double`
) has 7 different
`+()`
methods (and
`-()`
,
`*()`
, etc), one for every
`Numeric`
type that can be received as a passed parameter. [There's an extra
`+()`
method for handling a
`String`
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
`b`
parameter) as it is passed to the
`+-()`
method.

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
`b`
argument (
`def +-[U: Numeric](b: U) = ...`
) because a
`Numeric`
, expressed via a type parameter, can only add/subtract it's own type.

Is the only solution to create 7 different classes (
`PlusOrMinusShort/Int/Long/`
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`.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download