To provide historical context, in Scala 2.7, the numeric conversion was done as part of Predef.int2double
implicit conversion, as part of standard library. This was ok since the conversion is happening for the whole Int
type (not just literal expressions), but the conversion only happened when the expected type was already Double
. So List(1.0, 1 - 1)
or if (true) 1.0 else (n: Int)
would return List[AnyVal]
, AnyVal
etc.
We could’ve just ended inference to AnyVal
, but instead, Scala 2.8.0 designers mirrored the behavior of Java’s primitive conversion, and made a language spec change to introduce the notion of numeric widening:
If e has a primitive number type which weakly conforms to the expected type, it is widened to the expected type using one of the numeric conversion methods
toShort
,toChar
,toInt
,toLong
,toFloat
,toDouble
defined here.
This desire to avoid AnyVal
is confirmed in Dotty document:
The principal motivation behind weak conformance was to make an expression like this have type
List[Double]
:
List(1.0, math.sqrt(3.0), 0, -3.3) // : List[Double]
It’s “obvious” that this should be a
List[Double]
. However, without some special provision, the least upper bound of the lists’s element types(Double, Double, Int, Double)
would beAnyVal
, hence the list expression would be given typeList[AnyVal]
.
This too was ok since the conversion is happening for the whole Int
(not just literal expressions). Because secondary non-subtyping hierarchy was bolted on, there were some odd inconsistencies like #2841 by Paul
scala> List(1, 1L)
res0: List[Long] = List(1, 1)
scala> List(List(1), List(1L))
res1: List[List[AnyVal]] = List(List(1), List(1))
and a modified example from Jon
scala> List(0, 1, 2.0)
res4: List[Double] = List(0.0, 1.0, 2.0)
scala> 0 :: 1 :: 2.0 :: Nil
res5: List[AnyVal] = List(0, 1, 2.0)
The line of argument I am making is “if we drop weak conformance, then we must absolutely not treat literal expressions in a special way.”
We can do this in more than one way to handle List(1.0, 0)
, List(1.0, 1 - 1)
etc:
- Let it evaluate to
List[AnyVal]
, and explain to newcomers whatAnyVal
is. - Forbid inference to
AnyVal
, so the compilation fails. - Introduce subtype hierarchy to numeric types?