Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala inconsistence behavior for final vals with and without type ascription

Tags:

scala

I am using scala 2.10.3 and I noticed the following behavior

object TestConstantScala {

    final val str1 : String = "foo:" + number1
    final val number1 : Int = 123
    final val number2 : Int = 123
    final val str2 : String = "foo:" + number2

    def main(args: Array[String]){
        System.out.println(str1)
        System.out.println(str2)
    }


}

output:

foo:0  
foo:123  

And my question is why the order makes a difference. in addition if I omit the Int definition it returns to behave as normal

like image 289
Ehud Lev Avatar asked May 28 '14 13:05

Ehud Lev


1 Answers

Without the type ascription (: Int) number1 doesn't even exist as a field, and therefore doesn't need to be initialized. Instead, the compiler creates an accessor method that returns the value 123 directly, and in the constructor uses the literal value 123 to initialize str1.

Why does it create a field when there is a type ascription? It really makes no sense in this case, but sometimes type ascription can require code that does transformations of a value, like boxing a primitive or applying an implicit conversion. These operations should only be done once, both for semantic reasons (object identity, side-effects in the implicit conversion) and for efficiency. Therefore the result must be stored in a field.

So the behavior without a type ascription is an optimization for final primitive fields initialized to a constant value, and the compiler isn't smart enough to apply the optimization when a type ascription is present.

Here is a more minimal example:

object TestConstantScala {
  final val brokenStr: String = "foo:" + brokenNumber
  final val brokenNumber: Int = 123
  final val workingStr: String = "foo:" + workingNumber
  final val workingNumber = 123

  println(brokenStr)
  println(workingStr)
}

And here is the output from scalac -Xprint:constructors, showing the AST right after moving initialization into the constructor:

[[syntax trees at end of              constructors]] // test18.scala
package <empty> {
  object TestConstantScala extends Object {
    final private[this] val brokenStr: String = _;
    final <stable> <accessor> def brokenStr(): String = TestConstantScala.this.brokenStr;
    final private[this] val brokenNumber: Int = _;
    final <stable> <accessor> def brokenNumber(): Int = TestConstantScala.this.brokenNumber;
    final private[this] val workingStr: String = _;
    final <stable> <accessor> def workingStr(): String = TestConstantScala.this.workingStr;
    final <stable> <accessor> def workingNumber(): Int(123) = 123;
    def <init>(): TestConstantScala.type = {
      TestConstantScala.super.<init>();
      TestConstantScala.this.brokenStr = "foo:".+(scala.Int.box(TestConstantScala.this.brokenNumber()));
      TestConstantScala.this.brokenNumber = 123;
      TestConstantScala.this.workingStr = "foo:".+(scala.Int.box(123));
      scala.this.Predef.println(TestConstantScala.this.brokenStr());
      scala.this.Predef.println(TestConstantScala.this.workingStr());
      ()
    }
  }
}

Notice how there is no field for workingNumber, only an accessor, and how in the constructor workingStr is initialized with "foo:".+(scala.Int.box(123)).

like image 107
wingedsubmariner Avatar answered Nov 15 '22 07:11

wingedsubmariner