Consider the following code. The x
expression right after the inner let
block throws an error because x is not defined in that scope. This is expected given the scoping rules inside let end
block.
let
let
x = 0
end
x # throws "ERROR: UndefVarError: `x` not defined"
end
Now I modify the above and add some code in the outer let
block-- code that never actually executes.
let
let
x = 0
end
x # does this throw "ERROR: UndefVarError: `x` not defined" ?
if false
@assert false # just to demonstrate that this block never executes
x = 1 # try and comment this out
end
end
let
let
x = 0
end
x # does this throw "ERROR: UndefVarError: `x` not defined" ?
return
@assert false # just to demonstrate that this code never executes
x = 1 # try and comment this out
end
In the first of the two modified versions, the code in the if
block never executes, and in the second modified version, the code after the return
never executes. Yet the previous UndefVarError
is now gone. If you comment out the x = 1
expression, you get the error back.
Apparently, it doesn’t matter that x = 1
never executes because “scoping rules are static and not path sensitive”. see: unreachable code interacts with scoping rules and changes program behavior · Issue #56874 · JuliaLang/julia · GitHub
There are other examples, though the underlying reasons behind the behavior here might be different (see: implement local const · Issue #5148 · JuliaLang/julia · GitHub ):
function f()
if true
_f() = nothing
else
@assert false # just to demonstrate that this block never executes
_f() = 0
end
end
g = f()
@assert g() == 0
My questions:
- What does “static” mean?
- Is Julia two languages-- a static one and a non-static one? And does it mean that when one reads Julia code, one should read the static meaning of the code first and go over the same code to with a non-static perspective?
- Is it just Julia or is this an issue in other languages as well? I don’t know much Rust, but I tried to get into confusing scoping rules with it, and the language makes it hard because it makes variable shadowing very clear with explicit
let
statements. Scopes are clearly delimited with{}
and variables can’t go into a scope and get out of it alive. Basically, if you read the code sequentially, you can figure out what it does.