Scope behaviors in Atom (Juno)

in https://docs.julialang.org/en/v1/manual/variables-and-scoping/ it says

What does this code do? Hint: it’s a trick question. The answer is “it depends.” If this code is entered interactively, it behaves the same way it does in a function body. But if the code appears in a file, it prints an ambiguity warning and throws an undefined variable error. Let’s see it working in the REPL first:

julia> s = 0 # global
0

julia> for i = 1:10
           t = s + i # new local `t`
           s = t # assign global `s`
       end

julia> s # global
55

....

this is not the result i get in 1.5.1:

┌ Warning: Assignment to t in soft scope is ambiguous because a global variable by the same name exists: t will be treated as a new local. Disambiguate by using local t to suppress this warning or global t to assign to the existing global variable.
└ @ none:2
┌ 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.
└ @ none:3
ERROR: UndefVarError: s not defined
Stacktrace:
[1] top-level scope at ./none:2

it can hardly get any simpler than this

s = 0
for i = 1:10
    s += i
end

as it says in the documentation https://docs.julialang.org/en/v1/manual/variables-and-scoping/

Obviously the intention is to modify the existing global variable s . What else could it mean?

however, the in REPL 1.5.1 it causes warnings and errors

Not for me at least:

  | | |_| | | | (_| |  |  Version 1.5.1 (2020-08-25)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> s = 0
0

julia> for i = 1:10
           s += i
       end

julia>
1 Like

im on a mac, installed via the official process
julia> VERSION
v"1.5.1"

Are you actually pasting the code at the REPL prompt (should have no warnings) or using include to run it from a file (should have warnings and errors)?

4 Likes

i am pasting it

just found out,

when running REPL from terminal => all ok

when running REPL from juno (atom) => offending behaviour

both belonging to the same (presumably) julia installation, 1.5.1

So far, Juno has chosen not to implement this. https://github.com/JunoLab/Juno.jl/issues/174

It was very recently changed in vsCode: https://github.com/julia-vscode/julia-vscode/pull/1665

4 Likes

omg, this is highly irregular
i wish they would consider the impact on adoption of julia, from inconsistencies like that
many thx for ur answer

5 Likes

so now im running the code in vscode, via a script (tst.jl) and as you point out, that should produce an error. and it does (see below).

s = 0 # a global variable by the same name exists: s will be treated as a new local (in the loop)
for i = 1:10
s += i # Assignment to s in soft scope is ambiguous
end

i must say that i do not understand this scoping rule, it seems completely counterintuitive to me. if a variable s existed prior, the user clearly wants to redefine it; at best the parser could warn about it, if and only when a prior s existed of another type:

s  =  "hello"
s = 1  # ok to warn here
for i = 1:10
     s += i # no need to warn here
end 

whenever you have a construction like that, what is understood intuitively be 99% of all programmers (beginners and professionals alike) to have a certain expected behaviour and is interpreted just like that by the vast majority of other (scripting) languages, there should exist very good reasons for changing things.

on the other hand, if anything is broken in julia, please dont worry about backwards compatibility, please do break old programs. polish julia to be the gem it deserves to be and very nearly is. old programs can be compiled with old compilers / switches and #! preambles.

[Running] julia “/Users/p/tst.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.

└ @ ~/tst.jl:6

ERROR: LoadError: UndefVarError: s not defined

Love it or hate it, one just has to accept it at this point — it can’t be changed in Julia 1.x for non-interactive code without breaking backwards compatibility, which we won’t do.

Background: when Julia 1.0 was released, many people started complaining about the new scoping rules and there was extensive discussion. See e.g.

However, putting aside any discussion of the merits of one rule or another, the scoping rules could not be changed in files/modules after Julia 1.0 was released because of the backward-compatibility guarantee (until Julia 2.0 in the distant future). See also PSA: Julia is not at that stage of development anymore. As a compromise solution, after much debate, the behavior was changed in the interactive REPL (following an earlier experiment in IJulia) — and other interactive environments (vsCode and hopefully soon Juno) are now following suit — with a warning for code in files (scripts and modules).

At this point, there is no point in arguing about it — there is no argument you could possibly make that has not already been made dozens of times.

9 Likes

My suggestion is not to try… it is much more subtle than it appears. Instead,

  • stick in jupyter for interactive scripts which have top level for loops
  • feel free to transition to vscode for interactive stuff which uses the REPL or inline evaluation (which should now be consistent with jupyter, etc. )
  • before you ever run it as a .jl file on its own, you will want to have any loops in functions.
    • You would want to do that for performance reasons independent of this scoping rules.
    • The warnings will serve as a convenient way to remind you to make that change.

If you do that, you won’t ever have to think about this for a long time - if ever.

I think most of Atom/Juno’s developers are switching to VSCode, so I’m not sure if that inconsistency will be fixed.

It was already fixed today.

1 Like

I stand very much corrected…

@jperla, official documentation https://docs.julialang.org/en/v1/manual is not so good wrt the scoping rules. the prose is not clear. it contains seemingly contradictory info. at one point it says

This eliminated the notion of soft scope…

but it would seem that soft scope still exists… it would be good for the up-to-date documentation to only describe the present functionality. if needed, a discussion of past functionality and contrasting it with the present, should be placed in a totally separate document and linked-to from the current main documentation.

in general the prose is unnecessarily longwinded, talky, musing, joking (sarcastic?).

but most importantly, in the end, it is far from clear why the current (soft?) scoping rule exists. other than

  • a facility (as explained in this forum) for being able to paste-insert code chunk x into and inside another code chunk U without having x contaminate U
  • not having to bother about forgotten variables further up in a long script (as explained in the documentation)

… other than this, no reason, subtle or not, is offered.

if a subtle but substantial reason does exist, im very interested in knowing about it. maybe it will turn me into a staunch advocate of the rule. i am no rocket scientist but i do have a msc in computer science.

as it stands, the counter-intuitive and (to me) bothersome soft-scope remedy seems to me to be much more problematic than what it “fixes”: if you paste-insert code chunk x into code chunk U, it should be highly intuitive that x will contaminate U and that it is in that situation that it is reasonable to expect that a little mechanical effort be expended by the programmer/user, eg. by converting x into a function. as to “forgetting” about variables further up in a script, you have the parser to kindly point those cases out to you.

btw, before encountering the scopes chapter, i was actually impressed by the clarity, simplicity, brevity of the documentation. scoping should not be an “advanced” or subtle subject, neither intrinsically (=> leads to poorly designed code and/or code that is hard to maintain) nor because of a confusing documentation (=> leads to poorly designed code by those who do not abandon julia entirely). i am now reading the types chapter, and once again, i think this chapter could also be tightened, maybe contain half or 1/3 as much text. the chapter’s introduction seems to me to be more of a treatise on cs principles rather than a hands-on practical guide for non-programmers or beginning programmers. but experienced programmers also do not need that discussion, in that prominent place. if needed, it should be placed in a separate document and linked-to from the main documentation. however, the not-so-straightforward language continues through the whole chapter.

please consider my criticism as constructive. i like julia a lot and i hope for it to take off seriously. for that to happen, every friction should be eliminated as soon as possible. as a closing remark, i do not understand the commitment to backwards compatibility; there are many ways to honour that in practice for previous users and programs, eg. by feature/behaviour-freezing the current version into a new maintenance-line of future versions. only from a resource view point does it make sense not to offer two version-lines (“maintenance” and “new-feature”) but if the cost is a stagnating adoption-rate, is that a rational decision? from a business-model point of view, adoption-rate should be the most important KPI, by far.

personally, i think i can live with the quirk, for now.

Just note that for the purposes of this discussion, there is no evidence of this whatsoever. Julia is used by an ever widening audience, but even if it wasn’t, the causal connection to scoping rules (which are pretty arcane for most people) would need to be demonstrated.

It will be possible to argue about these things constructively at the appropriate time (in preparation for 2.0). There is little reason to invoke the “Julia is doomed unless …” line of arguments in the meantime.

You can find out the history with a trivial investment into researching the issue, eg here I collected some starting points. Just keep in mind that the issue is really complex, and the design space for scoping rules is full of trade-offs with a lot of use cases to cover, so it was a long process with a lot of discussion. IMO the manual may not be the right place for the history of scoping in Julia — it should just document the language as is.

That said, if you find the manual unclear, please consider making a PR. New users are in the unique position to do this, because they still remember the difficulties they encountered.

4 Likes

ok, thanks for reading and replying and indicating where to dig deeper - i will do that with interest. im afraid i do not have the time nor the qualifications to contribute at this moment beyond my novice considerations, aired here.

btw

@Tamas_Papp said: IMO the manual may not be the right place for the history of scoping in Julia — it should just document the language as is.

agree 100%