Search code examples
scalafunctionvariablesscope

Calling functions before they are defined (forward reference extends over definition of variable)


Consider this basic Scala example code:

object Test {
    def main(args: Array[String]) {
        inner()

        var x: Int = 5

        def inner() {
            println("x: " + x)
        }
    }
}

Trying to compile it produces the following error message:

test.scala:3: error: forward reference extends over definition of variable x
        inner()
        ^
one error found

Questions:

  1. What is a forward reference in this context and what does it mean for it to "extend of the definition of variable x"?
  2. Why does the former code raise this compile-time error?
  3. How is this error caught? Seems like the compiler has to implement some interpreter-like functionality and follow function calls!

This question is not really about the order of the definitions, but exactly when a function is called. It is completely legal for a function to be called before it is defined - but it suddenly becomes illegal if a variable is placed between the call and the function definition, and the function uses this variable.

I'd like this language feature explained! Why is it there? How does it work? Are there some other more complex examples - i.e. is it just a part of some other feature or a consequence of some rules?

What I imagine the compiler is currently doing:

  1. Check if the function is a closure that can access the variables of the current scope,
  2. check if it does in fact access variables in the current scope, and
  3. for each variable the closure accesses, check if the variable is defined before the call

Did I basically answer my third question? Is this how this behavior works? It seems to complicate the compiler a lot (especially if we consider cases with multiple levels of functions).

If that is the case, how does this integrate into the formal definition of the language, i.e. the grammar? It seems to me that the program I wrote is grammatically correct.


Solution

  • From http://www.scala-lang.org/docu/files/ScalaReference.pdf:

    The scope of a name introduced by a declaration or definition is the whole statement sequence containing the binding. However, there is a restriction on forward references in blocks: In a statement sequence s1 ...sn making up a block, if a simple name in si refers to an entity defined by sj where j >= i, then for all sk between and including si and sj,

    • sk cannot be a variable definition.

    • If sk is a value definition, it must be lazy