allidoiswin
allidoiswin

Reputation: 2589

How does Scala convert Int to Double?

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

Answers (1)

Andrey Tyukin
Andrey Tyukin

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

  • expression e = 42
  • type of expression T = Int
  • expected type 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

Related Questions