I expected the following definitions f1 and f2 to be equivalent but inner in f1 overwrites state variable from its outer scope despite being assigned in a let-block. Just trying to understand whether this is a bug? Tested on 1.8.5 and 1.9-rc1. Thanks!
function f1(x)
state = 0
inner(y) = let
state = y # overwrites state
state
end
inner(x)
state
end
function f2(x)
state = 0
function inner(y)
let state = y # fresh state variable
state
end
end
inner(x)
state
end
julia> f1(1)
1
julia> f2(1)
0
That’s not a bug, but a feature (?) of the let syntax, i.e., let only introduces new bindings for the variables declared in a comma separated list on the same line as the let itself (otherwise let is just like begin):
julia> function f1(x)
state = 0
inner(y) = let state = y # on same line => new binding for state
state
end
inner(x)
state
end
f1 (generic function with 1 method)
julia> f1(1)
0
This is one of the few cases where Julia syntax is white-space sensitive. Don’t ask me why the parser works like that … having been bitten by this several times, I tend to use let much less than I would in Lisp or so.
You’re right. let always introduces a new local scope, i.e., unlike begin which does not, and in addition creates new bindings for the variables in the same line as let (and only for those):
julia> function outer()
# scope with locals x, y
x = 1
y = 2
@show :before, x, y
let x = 3 # new binding for x
y = 4 # assignment to existing local
@show :let, x, y
end
@show :after, x, y
end
outer (generic function with 1 method)
julia> outer()
(:before, x, y) = (:before, 1, 2)
(:let, x, y) = (:let, 3, 4)
(:after, x, y) = (:after, 1, 4)
(:after, 1, 4)