Consider this snippet:
object A { val b = c val c = "foo" } println( A.b ) // prints "null"
As part of a larger program, this would lead to a failure at runtime. The compiler apparently permits the forward reference from 'b' to (uninitialized) 'c' but 'b' is left with c's original null value. Why is this permitted? Are there programming scenarios that would benefit from this feature?
Change the code to a straight sequence and the behavior changes:
val b = c val c = "foo" println( b ) // prints "foo"
Why is the behavior different? And why does this even work? Thanks.
Update 1:
The question came up how I ran the second example. I simplified the setup a bit and compiled it using Scala 2.9.0.1 inside IntelliJ IDEA 10.5.2 with the latest Scala plugin. Here is the exact code, in a freshly created and otherwise empty project, which I am using to test this, which compiles and runs fine in this environment:
package test object Main { def main( args: Array[String] ) { val b = c val c = "foo" println( b ) // prints "foo" } }
For what it's worth, IDEA also thinks (when I click "through" the reference to 'c' in val b = c) that I am referring to the (later) declaration of 'c'.
A forward reference occurs when a label is used as an operand, for example as a branch target, earlier in the code than the definition of the label. The assembler cannot know the address of the forward reference label until it reads the definition of the label.
2.11 Forward References Using an identifier before its declaration is called a forward reference, and results in an error, except in the following cases: When a goto statement refers to a statement label before the label's declaration. When a structure, union, or enumeration tag is used before it is declared.
Forward reference is when you declare a type but do not define it. It allows you to use the type by pointer (or reference for C++) but you cannot declare a variable. This is a way to say to the compiler that something exists. Say that you have a Plop structure defined in Plop.
In simple terms it means referencing (accessing a variable, calling a function) that is further down in the code file. static int x=getY(); static int y=5; static int getY() { return y; } x's value is set to the result of getY() getY() is called before y's value is set to 5. x's value is therefore 0 (default integer)
The body of a class or an object is the primary constructor. A constructor, like a method, is a sequence of statements that are executed in order -- to do anything else, it would have to be a very different language. I'm pretty sure you wouldn't like for Scala to execute the statements of your methods in any other order than sequential.
The problem here is that the body of classes and objects are also the declaration of members, and this is the source of your confusion. You see val
declarations as being precisely that: a declarative form of programming, like a Prolog program or an XML configuration file. But they are really two things:
// This is the declarative part object A { val b val c } // This is the constructor part object A { b = c c = "foo" }
Another part of your problem is that your example is very simple. It is a special case in which a certain behavior seems to make sense. But consider something like:
abstract class A { def c: String } class B extends A { val b = c override val c = "foo" } class C extends { override val c = "foobar" } with B val x = new C println(x.b) println(x.c)
What do you expect to happen? The semantics of constructor execution guarantees two things:
Output:
it will print "foobar" twice for more => https://docs.scala-lang.org/tutorials/FAQ/initialization-order.html
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