The let and do here are red herrings. Here are some simpler questions to nudge you in the right direction.
Let’s say I have:
function foo()
x = ...
end
This unambiguously creates a local variable named x inside foo(), right?
Now let’s say I have:
function foo()
println(x)
end
This unambiguously refers to some (hopefully existing) global variable named x, right?
Now what if I have:
function foo()
x = x + 1
end
What is x? If we follow the first example, it’s a local variable, but then the right-hand side makes no sense since that local variable doesn’t exist yet. If we follow the second example, then it’s a global variable, but now we are silently modifying a global variable, which the language does not allow. If we follow both examples, somehow x is both a local and a global variable at the same time, which is even worse. That’s why this situation is an error in Julia, since there’s no obviously right thing to do.
If you want x to refer to the global variable, then you need:
function foo()
global x
x = x + 1
end
That works with no issues. If you want x to be a local variable, then you can’t also have it refer to the global variable with the same name.
A let block creates a new local scope, so you can do:
julia> let
a = 1
function foo()
a = a + 1 # updates `a` in the parent local scope.
end
foo()
println(a)
end
2
as explained in Scope of Variables · The Julia Language