Local vs global scope again

I still think the solution I proposed would work in this case (which is discussed in the 1.5 blog):

I quote:
Despite the “on paper” simplicity of the 1.0 scope rule, many people found it unintuitive and annoying. Consider the following example:

s = 0
for i = 1:10
    s = s + i
end
println(s)

One would naively guess that this prints the number 55 , and prior to Julia 1.0 that’s exactly what it did. But in 1.0, we consistently made for loops introduce new local scopes, which means that this code would throw an undefined variable error because s is deemed to be local to the loop: since there is no pre-existing local s (only a global s ) and s is assigned in the loop, it is local and the first iteration of the loop tries to access an undefined local s .

My proposed solution was to check if there was a “read” of a variable before there was a “write” to it in the loop. In this case there is: s + i “reads” the value of s. Then, that would mean that the variable must have existed prior to entering this loop, and s would have become global inside the loop.

So the simple rule “if you assign to a variable in a local scope (function or loop) and there’s no local by that name, then that assignment creates a new local variable” would work just fine in a global scope, if supplemented with “was there a read first?”.

2 Likes

Is there a question?

I’m just saying that it might have been a better solution than the “soft scope”, which in my mind is less intuitive. Distinct rules in the command line and in files I think are counterintuitive.

Okay, I recognize that it is too late now, but I was just trying to explain the scopes to a student. It wasn’t easy.

It was discussed and for a while it was my preferred possible fix. However, a number of legitimate potential issues were raised in that thread and elsewhere (it’s been discussed so much it’s hard to keep track). I won’t repeat them here.

Jeff prototyped it and we played around with it for a while to get a feel for the behavior. It was in fact kind of fussy and brittle. When you account for all the possibilities, the rule is not so simple to explain or to implement (that patch adds an enormous amount of Scheme code to the parser). It also fails to address the fact that when someone assigns to a variable in a loop that is already a global, it’s actually ambiguous what they mean: did they intend to clobber the global, or did they want a local that shadows the global? Yes, we could make it work in some cases like this example, but that wouldn’t address the deeper ambiguity. So after a lot of discussion, Jeff and I decided against it. It just didn’t feel right.

We came up with the current solution as an alternative and some time later prototyped it. In the REPL it uses a proven heuristic from 0.6 and just does the convenient thing. In a file, on the other hand, it forces you to disambiguate in exactly the case where the heuristic is necessary and where the problematic ambiguity exists. This one “feels right”: it’s relatively simple and it prevents people from accidentally writing broken and/or confusing code. In PkgEval runs (with the warning made fatal) we found that it hardly ever triggered a warning in package code, and when it did, that code was usually actually buggy.

I’m also very done with conversations on this subject unless they are “This works so well, thank you very much!” :kissing:

23 Likes

This is just not nearly as simple as it sounds. “Does this image contain a cat?” also sounds like a simple question. First in time or in space? Does the read have to actually happen, or just be possible in some way? How does it combine with gotos, macros, and closures? What if there are no reads? If there are no reads, you probably meant to assign the outer variable? This approach does handle some simple examples well, but as soon as code gets a bit more complex, good luck. I just don’t agree that “does a variable of that name already exist?” is harder to explain than dataflow analysis.

Right, so why not just directly consult whether the variable already exists, instead of trying to infer that from a subtle property of code that uses it?

10 Likes

I think that for a while, asking that proposals about scope come with a PR to the Julia repo (with tests passing) would make sense.

The current solution may be close to at least a local optimum in the sense that it has become difficult to discuss improvements using short code snippets focusing on a particular case.

5 Likes

I hear you all. But the student upon hearing the explanation of the scope behaviour stated “that is kind of ugly”. It hurt.

2 Likes

I had professors who would handle similar situations by asking that the student reviews the relevant discussions, compiles a compendium of code snippets that were used to illustrate various trade-offs, with a table summarizing how each scope proposal would work in each situation. By Monday :wink:

8 Likes

I object strongly to the reflexive attitude of superior insight into design decisions. Such criticisms are cheap and amount to a tax on those who do the hard work of building something that people care about. Regardless of the merits of the student’s criticism, I would favor guiding them in a direction of greater self-skepticism.

4 Likes

Being skeptical is a virtue, being dismissive is a vice. Sometimes the line is blurry.

This particular example veers toward the latter, though.

3 Likes

:man_shrugging:t3: I invite them to come up with something better along with an explanation of why it’s better.

If you want something conceptually “clean” then the 1.0 rule is that. Everyone complained about it. You want something complex but convenient? The 0.6 rule is that. People complained about that too.

I’m approaching a decade of people complaining about different scope rules. Anyone who doesn’t like the rule for implicitly deciding what is local can use local to declare everything. Problem solved.

15 Likes

This will not change. This is the backside of every great success and achievement, especially for complex ones. Try to think of this not as complains. It is an expression of great involvment for example, or what ever fits for you. Frustration will let you overlook the vast amount of triumph Julia is (admitted a bit of exaggeration :grinning:)!

On the other side I don’t see OP as dismissive. Not at all. It isn’t skepticism or criticism. All negatively annotated. It is just only another contribution to a public debate of a specific detail. This is science and development, it is never finalized.

6 Likes

Before getting into scientific computing and Julia I mostly did web development in JavaScript/Node.js. I would routinely get burned by some scoping issue (especially before they introduced let and const) that would take forever to track down. I don’t recall this ever having happened to me in Julia. On very rare occasions I have to stick a global in front of a variable name somewhere to achieve the desired behavior but, for the most part, I’ve found that scoping in Julia just works for me. So much so that I’ve never actually spent much time reading about how it works :blush:. In contrast, I read extensively on scoping in JavaScript because it was necessary to understand it deeply in order to keep from getting burned over and over :wink:

6 Likes

My apologies to all Julia core developers. I shouldn’t have vented here.
I do like simple rules (OCD?), but that boat has sailed.
Now it’s the time to get used to the way things work. :slightly_smiling_face:

6 Likes

It seems like the current rule is pretty simple…

1 Like

If you are referring to my post, I was talking about the student (and in reply to the previous post), not about the OP himself.

1 Like

All true, but this is setting up a false comparison: The non-REPL julia rules vs. a language without loops introducing scopes.

It is now 2 years since IJulia patched things to add the v0.6 scoping rules back in. How many issues have you seen in discourse/stack exchange/etc. on Jupyter scoping rules since then. I have seen zero. Literally none. I may have missed something, but it might actually be zero. Anytime jupyter it is mentioned in a quetsion/comment it is always the “Jupyter does what I expected. I don’t understand why running it as a .jl file doesn’t behave like jupyter”.

That doesn’t mean it is possible to immediately switch everything over to jupyter/1.5 REPL rules (as it is breaking behavior and there were legitimate tradeoffs which led to the decision for v1.0), but it suggests there was a previous design does have a coherent set of rules given the various tradeoffs involved.

2 Likes

I see, it wasn’t clear.
Looking back it was the quote of the students:

which was called dismissive I guess.
Yes it is, but they are strudents in a (probably) beginners course, and I guess, it was just said without so much thinking. It should not be taken serious by the words.

But OP point still is valid as a general point of view. And it should be explicit said, that this type of topic (OP) is always welcome, now and in future, because Julia must always develop further. There should never be a “it is now what it is forever so be quiet”.

To be clear: it wasn’t said, that OP should not be created. I just felt some bad tendencies, first when “dismissive” wasn’t clearly pointed to the quote “ugly”, second when I felt some kind of frustration with the topic in general.

1 Like

It was mainly a comment on the previous post, with just a side note on the student’s remark. I meant to reflect on the sometimes thin line between teaching skepticism and avoid being overly dismissive.

1 Like

I like the selected solution a lot: :bouquet: “snippet code” just works - no need to know/care about scopes, :bouquet: more involved code in files is “properly scoped” and :bouquet: there is a warning when there are ambiguities.

As a result one can learn about “the finer scope” at a later time (or maybe even skip it :relaxed:).

8 Likes