REPL and for loops (scope behavior change)


#1

In the REPL, to test some code, I often do things like

julia> a = 0
0

julia> for i=1:3
           a+=i
       end

But under Julia 1.0 this will now throw an error

ERROR: UndefVarError: a not defined
Stacktrace:
 [1] top-level scope at ./REPL[2]:2 [inlined]
 [2] top-level scope at ./none:0

The fix is to define

julia> global a=0

which is quite annoying. Is there a way to implicitly make every variable declaration in the REPL global?


#2

In the REPL, variable definitions are global - however, for introduces a new scope. See scoping.

To access the global variable, use global at the first use of the variable in the loop.


#3

Thank you, I knew about the scoping, but not that you could use global in the loop!
But still this somehow feels broken, I cannot simply copy code out of a function in the REPL to debug it :frowning: (In the lack of a proper debugger)


#4

It is annoying… a workaround for the copy paste is to put everything inside a let block:

julia> let
       a = 0

       for i=1:3
           a+=i
       end
       a
       end
6

#5

This really worries me. When we teach Julia, students typically start to code in global scope (in Jupyter) and at some point I tell them, “and if you now put everything in a function it might be even faster”. However now, I have to tell them about scoping and ‘global’ very early on, as they have to put 'global’s in the first place and remove them in the function, which I find suboptimal. Although I think I see the technical benefit, it feels to me that this is maybe trading too much of the user’s convenience (I don’t know any interactive language where you would have to do something like this). After all Julia has not just been great for performance but also simple, naive usage. Just my two cents.


#6

I’ll be an instructor for Engineering freshmen this fall and I’ll be teaching Julia. I have been thinking about this problem.

Since it is very “Julian” to write small functions I think the first thing I’m going to do is teach them how to write functions. Like tiny functions.
From that I’ll explain scoping rules, and how to refer to a variable defined outside the function.
Then I think of introducing for loops, if statements etc using small functions that need those.
Only after that I’d explain them that everything is a global in the REPL, so they have to be careful with this gotcha when tinkering on the REPL.

The point is they really should be writing small functions since the beginning.


#7

Hi Guys, i’m having the same problem in Jupyter Notebook. I’m creating my own Julia tutorial following the youtube “Intro to Julia” video.

In a code cell I made it:

n=0
while n < 10
    println(n)
   n+=1
end

And the result was:

UndefVarError: n not defined

Stacktrace:
[1] top-level scope at ./In[18]:3 [inlined]
[2] top-level scope at ./none:0

I’ve tried to put the local status in any place but the result still goes wrong, the solution came following @saschatimme instructions (or at least how I interpreted that), like below:

n=0
while n < 10
    println(n)
   global n+=1
end

I’m newbie in programming.

This problem is a kind of bug?

Am I doing something wrong?

Thanks since now guys!


#8

It is not a bug.
It worked in Julia 0.6, in Julia 0.7 it gave a deprecation warning that you must add global, and in 1.0 it throws an error.

I really like favba’s idea of starting by introducing functions and teaching them to write tiny functions. Sounds like that’d also end up encouraging good coding habits.


Usage of loops for defining new variables
#9

This was a tough decision that we deliberated over for a very long time and had many conversations about. The old behavior was carefully designed to make the behavior of loops and other scope-introducing constructs the same in global scope or in the body of a function. However, the down side was that to accomplish this, whether an assignment inside of a loop or other non-function scope assigned to a global variable or created a new local variable depended on whether a global binding for that name already existed or not—which is not, in general, a statically predictable property. This also created a distinction between the kind of scope which a top-level loop or other non-function scope-introducing construct created and the kind which functions created. This behavior was widely misunderstood and often complained about when people were trying to wrap their heads around the scoping behavior.

Now, in 0.7/1.0 there is only one kind of scope: functions and loops and other scope constructs are all the same. So that’s much simpler and now whether a variable is local or global is always statically predictable. But the down side is that the same code in a function or on global scope do not behave the same anymore. This trade off is unavoidable given the way local variable are implicitly introduced in Julia—we really explored all the possible options for this. Languages that require you to declare local variables don’t have this problem but then again declaring the occasional global is a lot less difficult than declaring every local.

There is one possible way to recover the old ability to paste code from a function body into the REPL and have it behave the same, which is to automatically wrap the code in a let block to make the behavior like that in a function body. We’ll probably experiment with this in the future and see how well it works. It feels a bit weird to special case the REPL like this but it may be better.


#10

Thanks Chris, really appreciate your atention.

Another question, the way I did is the right way? Cuz I tried to use “local” but didn’t work. Using global will not cause any trouble with other variables with same name in another loops or function?

Thank very much, again!


#11

And it begins: https://stackoverflow.com/questions/51930537/scope-of-variables-in-julia/ :sweat_smile:


#12

I’m hosting a hands-on introduction to Julia for PhD-students in energy systems modeling. The whole mini-course will have three 2.5-hour sessions, and I just came back from the first session an hour ago. Four out of 12 students were stumped by this issue completely independently of each other. All four worked interactively in the REPL to make their code work before saving it to a file. None of these four had enough programming experience to know what scoping meant, but all are capable of hacking together Matlab scripts.

The bottom line is this. Programming newbies who try everything out in the global scope of the REPL find this scoping behavior completely counter-intuitive. This makes the language seem unfriendly, bordering on broken in this particular case.

As for me, I think scoping rules are an intermediate level topic and shouldn’t be necessary to bring up in an introductory-level course. If you just tell students to wrap all code in functions, even in the REPL, then suddenly the language seems quite a bit less interactive and harder to work with.

I understand why the scoping changes were made in 1.0, but I hope that automatic let-block-wrapping idea that Stefan mentioned gets implemented and released ASAP. It may even be worth making a 1.01 bug fix release just for that. Because I’m pretty sure I lost a third of my potential new Julia recruits just because of this.


#13

You really cannot win. We used to get so much flack for the scope rules being slightly complicated specifically to make this case work in an intuitive way. So we fix that and now we have this. :pensive: In any case, I think that the current behavior, though less convenient, is the right one since it is the one that is statically resolvable. We should experiment with the automatic let wrapping in the REPL, however.


#14

Can that (let wrapping) be done in 1.0.1, instead of waiting for 1.1?
It is only the REPL :wink:

I am a big fan of the new behavior, the old “spooky action at a distance” scoping rules was one of the biggest warts on a beautiful language, thanks for taking the hard decision to fix this in v1.0.0…


#15

Yes, this does sound very interesting. It could help with closures from the REPL too.


#16

Here is a Github issue if you’d like to follow along there.


#17

This post was flagged by the community and is temporarily hidden.