I was expecting this to raise an error as I'm trying to use x
before assigning it. Here's the Scala 3.3.1 REPL:
scala> val x: Int =
| val y: Int = x + 10
| y
|
val x: Int = 10
Why is the result 10? When Scala evaluates the x + 10
, it assumes x=0
?
It's happening because of the same REPL-trickery that allows you to compile and execute
> val x = 1
> val x = 2
in the interactive REPL-mode: the val
s are not compiled as local variables inside of function bodies, but as members of wrapper objects, as described here.
In your particular case, when you type your example
scala> val x: Int =
| val y: Int = x + 10
| y
|
into the REPL, it gets wrapped into a synthetic "execution template"-object, which behaves roughly as the following script:
object replInputLine$1 {
val x: Int =
val y = x + 10
y
println(x)
}
@main def entry(): Unit =
replInputLine$1
What is happening in this script? Well, the replInputLine$1
-object is initialized. As always during object-initialization, the member x: Int
is allocated and initialized to 0
during the allocation of the object, and then the actual object initializer is executed, which updates x
to 10
.
Since x
is an object member, and not a local variable in a method body, it can be accessed without problems.
If instead you tried to use the same code snippet inside of a method body, you would get compilation errors, as the following example shows:
@main def entry(): Unit =
val x: Int =
val y: Int = x + 10
y
println(x)
This will fail the compilation with the message
Reference error: [...] x is a forward reference extending over the definition of x
So, it's just an artifact resulting from the fact that REPL treats val
s as members of a synthetic object instead of treating them as local variables.
Playing "language-lawyer" games with the REPL is pointless and boring: if you want to understand what the compiler is doing "for real", don't test it in the REPL, write a separate program, then compile it.