Reputation: 2589
val d: Double = 42
When I try to find implicit conversion via intellij, nothing interesting comes up. Also, Int
isn't a subtype of Double
. So how does Scala do it?
Upvotes: 8
Views: 5175
Reputation: 44957
Long story short: it's not an ordinary implicit conversion on some companion object, the numerical types get a special treatment.
If we run scala -print
on this script:
val d: Double = 42
we obtain:
package <empty> {
object Main extends Object {
def main(args: Array[String]): Unit = {
new <$anon: Object>();
()
};
def <init>(): Main.type = {
Main.super.<init>();
()
}
};
final class anon$1 extends Object {
private[this] val d: Double = _;
<stable> <accessor> private def d(): Double = anon$1.this.d;
def <init>(): <$anon: Object> = {
anon$1.super.<init>();
anon$1.this.d = 42.0;
()
}
}
}
In the desugared code, we see a double literal 42.0
, but no invocations of any conversion
functions (e.g. from Predef
). Thus, the conversion from Int
to Double
must take place not
at runtime, but at earlier stages of compilation.
The section 3.5.3 of the specification
tells us that Int
weakly conforms to Double
because of the transitivity of the weak conformance relation <:w
:
Int <:w Long <:w Float <:w Double
Furthermore, Section 6.26.1 (Value Conversions)
tells us that rules for numeric widening are applicable if an expression e
of type T
appears in position where an expression of
type pt
is expected and T
weakly conforms to pt
. In this case, we can apply the rule with
e = 42
T = Int
pt = Double
Thus, 42
is converted to 42.0
using toDouble
. Since it's a constant that can be processed at compile time,
we don't see the toDouble
in the desugared code. However, if we desugar a similar program with a non-constant
value
val d: Double = (new scala.util.Random).nextInt(42)
we obtain:
package <empty> {
object Main extends Object {
def main(args: Array[String]): Unit = {
new <$anon: Object>();
()
};
def <init>(): Main.type = {
Main.super.<init>();
()
}
};
final class anon$1 extends Object {
private[this] val d: Double = _;
<stable> <accessor> private def d(): Double = anon$1.this.d;
def <init>(): <$anon: Object> = {
anon$1.super.<init>();
anon$1.this.d = new scala.util.Random().nextInt(42).toDouble();
()
}
}
}
and the toDouble
is there, as specified.
Upvotes: 14