You will find long and exhaustive threads discussing how Julia ended up with the current scoping rules and behavior, and why it is a good compromise between the pros and cons of many other alternatives. Just search for "scoping rules" in the Discourse forum.
My perhaps didactic explanation on the choices made is below. I understand that scopes, at least in this context, can be understood as blocks of code that can be compiled and executed independently. One wants the compiler to have as much information as possible concerning the variables of that code block, such that it can specialize the code to the types of variables inside the block. With that image in mind, we have:
Ideally one would like that a loop like
s = 0 for i in 1:3 s = s + i end
worked always and modified
sas intended. Yet, in Julia, for performance reasons, the
forloop introduces a new scope, where the variables may be inferred by the compiler to remain with constant types during the loop execution. If the variable
sis global, that means that its type can be changed from outside the loop. Therefore, writing a loop that makes reference to a global variable cannot be simply accepted without notice.
There is no problem in writing such a loop inside a function, because there the types of the variables are constant except if modified by some operation inside the function itself. If they are not, the compiler can realize that and the loop is fast. No problems there.
That loop written in the global scope will be problematic (slow) because
smight not have a constant type. That is, since the type of
scan change outside the loop, the compiler cannot specialize the operations of the loop to the type of
s. Thus, one should warn the user that that is not a good programming style. Previously, because of that, it was required that the use of the global variable was explicit:
julia> s = 0 for i in 1:3 global s s = s + i end
However, this was inconvenient, because one was not able to copy and paste a code from a function to the REPL. Thus, since Julia 1.5, it was decided that at the REPL the code without the explicit
global sdeclaration will work. The
svariable is still global and the loop will not be efficient, but this in this context it is acceptable because nobody writes critical code directly to the REPL.
That leaves the possibility of writing such loops inside files, outside functions. For example, defining a file
myloop.jlwith that loop coded directly inside it, and executing the code in the global scope with:
A programmer can be tempted to write critical code inside a file in such a way and, while that is not impossible, it must be discouraged. Thus, the choice was to raise a Warning and an Error associated with the possible scoping problems of that loop if it is written as if it was in the global scope of that file:
julia> include("./myloop.jl") ┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable. └ @ ~/Drive/Work/JuliaPlay/myloop.jl:3 ERROR: LoadError: UndefVarError: s not defined
The warning is clear and is saying: don't do that, unless you are really really aware of its consequences, and in that case declare
sas global explicitly.