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
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
Implicit Type Conversion is also known as 'automatic type conversion'. It is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present.
Numeric widening is an implicit conversion between two numbers of different types (Int and Double, for instance) useful for many cases, but it may be an huge source of bugs.
Implicit conversions in Scala are the set of methods that are apply when an object of wrong type is used. It allows the compiler to automatically convert of one type to another. Implicit conversions are applied in two conditions: First, if an expression of type A and S does not match to the expected expression type B.
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.
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?
^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With