Why is it possible to assign recursive lambdas to

2019-02-25 20:11发布

问题:

In the following statement the val f is defined as a lambda that references itself (it is recursive):

val f: Int => Int = (a: Int) =>
    if (a > 10) 3 else f(a + 1) + 1 // just some simple function

I've tried it in the REPL, and it compiles and executes correctly.

According to the specification, this seems like an instance of illegal forward referencing:

In a statement sequence s[1]...s[n] making up a block, if a simple name in s[i] refers to an entity defined by s[j] where j >= i, then for all s[k] between and including s[i] and s[j],

  • s[k] cannot be a variable definition.
  • If s[k] is a value definition, it must be lazy.

The assignment is a single statement, so it satisfied the j >= i criteria, and it is included in the interval of statements the two rules apply to (between and including s[i] and s[j]).

However, it seems that it violates the second rule, because f is not lazy.

How is that a legal statement (tried it in Scala 2.9.2)?

回答1:

You probably tried to use this in the REPL, which wraps all contents in an object definition. This is important because in Scala (or better: on the JVM) all instance values are initialized with a default value, which is null for all AnyRefs and 0, 0.0 or false for AnyVals. For method values this default initialization does not happen, therefore you get an error message in this case:

scala> object x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
defined object x

scala> def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
<console>:7: error: forward reference extends over definition of value f
       def x { val f: Int => Int = a => if (a > 10) 3 else f(a+1)+1 }
                                                           ^

This behavior can even lead to weird situations, therefore one should be careful with recursive instance values:

scala> val m: Int = m+1
m: Int = 1

scala> val s: String = s+" x"
s: String = null x