Scope and global in REPL (julia 0.7)

x=1;
while x<10
    x+=1
end
┌ Warning: Deprecated syntax `implicit assignment to global variable `x``.
│ Use `global x` instead.
└ @ nothing none:0

I think while should not open a new scope of variables. In more complex real world code this would force us to add many global (or local) statements for every variable which is altered inside the loop.

Another one where I am unhappy about:

a=zeros(Int,5)
i=1
a[i]=1
for i in 2:5
	a[i]=i
end

gives:

┌ Warning: Deprecated syntax `implicit assignment to global variable `i``.
│ Use `global i` instead.
└ @ nothing none:0
┌ Warning: Loop variable `i` overwrites a variable in an enclosing scope. In the future the variable will be local to the loop instead.
└ @ nothing none:0

Again, the “global i” is annoying, but using it:

a=zeros(Int,5)
i=1
a[i]=1
for i in 2:5
	global i
	a[i]=i
end

still gives:

┌ Warning: Loop variable `i` overwrites a variable in an enclosing scope. In the future the variable will be local to the loop instead.
└ @ nothing none:0

and this is even unclear how to avoid this warning.

Is there something I miss? I am the only one who is wondering why the above scope examples are a good thing?

4 Likes

The crucial thing is global issue. In real world you will probably have loop along with the variable it references to defined inside a function and in such a situation while loop will just work and in for loop there is outer keyword that handles this case, see https://docs.julialang.org/en/latest/manual/variables-and-scoping/#For-Loops-and-Comprehensions-1.

Finally, if you do not want to define a function you can use let block e.g.:

julia> let
       x=1;
       while x<10
           println(x); x+=1;
       end
       print(x)
       end
1
2
3
4
5
6
7
8
9
10

EDIT:
One more comment about let block.

This form:

let x = 1
end

creates new binding for x that is visible only inside let block, but this form:

let
x = 1
end

will give you the same warning as for and while loop if x was earlier defined in global scope.

4 Likes

Thanks for the help. Especially the outer keyword was completely hidden from my radar.

Still I think the whole scope thing, as it is now in 0.7, will bring a lot of questions in future. As said in the docs:

The distinction between inheriting global scope and nesting local scope can lead to some slight differences between functions defined in local vs. global scopes for variable assignments.

The implications seems to be subtle and therefor prone to headaches especially for beginners or people coming from other languages.

Anyways thanks for the hints, they solve my special issues I have currently changing from 0.6 to 0.7.

4 Likes

I think part of the difficulty is due to the deprecation warnings. When those are removed, the case where global i is declared in the loop can work.

The new rule around this is actually very simple: global variables are not automatically inherited for writing by inner scopes. That’s all.

The other relevant change is that for i acts like a repeated version of let i; i.e. the loop variable will always be a new variable. This helps avoid action-at-a-distance in code, and makes it easier to switch between for loops and functional constructs.

2 Likes