12

I was always figuring implicit conversions over multiple levels is not possible in scala (unless you define view bounds: http://docs.scala-lang.org/tutorials/FAQ/context-and-view-bounds.html)

But it seems that there is a flaw in the type system or an inconsistency. Following an example (adapted from How can I chain implicits in Scala?)

class A(val n: Double){
  def total = n + 10
}

object T1{
  implicit def toA(n: Double): A = new A(n)
  val i : Int = 5
  println(i.total) //Why does this work?
  println(5.2.total)
}

I do not really understand why the implicit conversion from Int -> Double -> A works. Can someone please explain the reasons? Thanks

Community
  • 1
  • 1
Robert Stoll
  • 371
  • 3
  • 11
  • 5
    widening conversions are different from implicit conversions: https://developmentality.wordpress.com/2011/01/24/scala-type-inferencing-gotchas/ – Gábor Bakos Jan 31 '15 at 13:28
  • In the language reference weak conformance might help understanding (these are not implicit conversions): http://www.scala-lang.org/files/archive/spec/2.11/03-types.html#weak-conformance and this question: http://stackoverflow.com/q/3094380 – Gábor Bakos Jan 31 '15 at 15:03

2 Answers2

10

It happens via a different mechanism, unique to the numeric types, called numeric widening.

SLS 6.26.1 Value Conversions says:

The following five implicit conversions can be applied to an expression e which has some value type T and which is type-checked with some expected type pt.

Static Overloading Resolution

Type Instantiation

Numeric Widening

Numeric Literal Narrowing

Value Discarding

View Application

Dynamic Member Selection

(Okay, that's more than five....not sure why :)

The one of interest is 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.

3.5.16 Weak Conformance says

In some situations Scala uses a more general conformance relation. A type S weakly conforms to a type T, written S<:wT, if S<:T or both S and T are primitive number types and S precedes T in the following ordering.

Byte  <:w Short
Short <:w Int
Char  <:w Int
Int   <:w Long
Long  <:w Float
Float <:w Double

So println(i.total) becomes println(i.total.toFloat) because Int <:w <: Long <: Float.

Java (and C# and many other languages) have numeric widening, and Scala decided to keep it.

Note that the reverse does not work: a Float cannot be implicitly converted to Int via this way, since magnitude could be lost; it's not a "widening".

You can add -Ywarn-numeric-widen and get a warning when this happens.

Community
  • 1
  • 1
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
1

As Gabor already commented, this is due to numeric widening. If you run with the -print option, you will see that a .toDouble is appended to the i, which then allows it to use the toA implicit. You can run scalac with the warn-numeric-widen and this will at least give you the following:

<console>:14: warning: implicit numeric widening
     println(i.total) //Why does this work?
             ^
Justin Pihony
  • 66,056
  • 18
  • 147
  • 180