New scope solution

I meant that without global it is stay as it is. With global it apply heuristic. Which one is open question (although I slightly prefer @jballanc’s as simpler).

I’m not a fan of what I just proposed, it was just what I understood. I don’t think global for with implicit globals is a good idea because it will leak variable declarations into the containing scope.

Forgive me if I’m wrong, but I thought for and while are syntactic sugar for let and conditional @goto (plus some iterate calls in the case of for); and if, else, etc are syntactic sugar for conditional @goto.

For that reason, the rule needs to work post-lowering and be clear on this lower level, as well as imply a sensible high-level rule (at the very least, the rule should be invariant under “partial lowering”, i.e. replacing high-level constructs by their low-level targets). Since the rule should be invariant under rearrangement of basic blocks, the second example follows. Or did I get this wrong?

In principle, your rule is on the control flow graph, not AST (that’s why it is more complicated, and also why it should be accompanied with tools to view the CFG; current tools to view the AST are not helpful anymore to debug scoping issues). So it is not syntactic, but close enough not to matter outside of corner cases.

PS.

question what language construct @goto and @label is I could probably ask in future in another topic. :wink:

Goto does what’s written on the label :wink: Sorry for the pun. A label labels a position in your code (so it does nothing). Goto goes (jumps) to a position; you need to label the position in order to tell goto where to jump to. This is super useful for e.g. breaking out of nested loops, and imho far cleaner and more readable than pulling along a bool (typical found) and doing multiple found && break and a final if found ... else end. Also, it is the low-level construct that loops are syntactic sugar for.

Can you please clarify for which constructs and contexts this would apply?

Eg

  1. for, while, try-catch-finally, let, comprehensions, broadcast-fusing,
  2. only for the REPL? or all module/baremodule?

are there examples of for or let blocks evaluated in a module scope where we definitely would want the variables in those blocks to be local?

Yes, this happens frequently when you copy/paste method bodies to the REPL and wrap them in a let. For example, in a function I can use first as a variable without conflicting with the function named first; if first is local to the let, then there is also no conflict and my copy/paste debugging works. If in contrast first is global, I have to change the name of that variable before I can debug in this fashion.

(@StefanKarpinski, I’ve not yet had time to really sit down and think about this carefully; I will try to do so soon, though I suspect you’ll get all the feedback you need from others. No matter what, kudos for coming up with devious solutions!)

2 Likes

2 posts were split to a new topic: Side discussion on gotos

A post was merged into an existing topic: Side discussion on gotos

This was precisely the solution previously proposed here:

All scope-introducing constructs that occur outside of a function body.

  • only for the REPL? or all module / baremodule ?

Both. One of the goals is not to have different behaviors in different places. Since that inevitably leads to a series of questions about “why does this work in the REPL but not in my script/module.” In particular, since “scripts” (aka programs) operate in Main, which set of rules should apply to them? Are they interactive or non-interactive?

7 Likes

:100:

8 Likes

Ok, yeah so I did miss something…apologies! In my defense, it’s been a bit difficult as a bystander to try and follow all the proposed solutions and counter-examples. Glad we have some individuals focusing so much of their attention on this issue. From the multiple issues/threads, it’s obviously something that’s hit a nerve and should be carefully considered…so thanks!

3 Likes

So, this has me wondering how much of the trouble around this issue and proposed solutions has to do with scoping, and how much has to do with shadowing? I wonder if it’s worth considering these as separate issues? Maybe we turn Julia into a LISP-2 in v2.0? (…ok, that last one’s a joke :wink: )

2 Likes

This transformation must operate very early on AST, before lowering. For technical reasons, it has to since it has to come before closure conversion. But more significantly, it would also be very broken if the behavior of such a feature depended on details of lowering; lowering is an implementation detail, not a feature of the language.

2 Likes

How are these different? If a local name shadows a global one, doesn’t that mean that it has local scope? Note that talking about this issue being about scope is a bit misleading: no scopes are changed on any of the proposals. The only question is about automatic inference of local and global annotations when x = is seen in various contexts.

(Also, Jeff and I both found the Lisp-2 joke very amusing :slight_smile: )

2 Likes

Only allowing the introduction of new variables with either a let (more or less equivalent to cl:let) or a global (= cl:defparameter) would also solve this problem, both technically and then socially (everyone would then beg the core devs to return to the 1.0 semantics, and no sane person would bring up the issue again for fear of causing something similar). :wink:

7 Likes

Does this include for loop variables which are not used inside the loop (or a comprehension)?

println("Wake up")
for i in 1:3
    println("5 more seconds")
    sleep(5)
    println("Wake up")
end 
println("Fine!")

No, loop variables are always local.

4 Likes

If the question is: “Why not allow variables in for loops to always have global scope?”, it seems like the answers fall into one of two categories:

  1. Because we would have to be more careful with the names we use, since a re-definition of a global variable within a for loop would last for the duration of the script/REPL-session (shadowing).
  2. Because this would generate a lot of values and objects that might consume considerable memory and never go out of scope to be collected (scoping).

Obviously, an elegant solution that manages to address both categories would be desirable, but if it proves difficult to “kill two birds bugs with one stone patch”, perhaps addressing these concerns separately could yield better results? An example I provided…somewhere in one of these threads…was how Ruby managed to address the issue of shadowing in lambdas, while still having many unresolved problems around scoping.

1 Like

In my humble opinion of a discussion that excludes me due my lack of experience in the intricacies, i could imagine a rather layman’s solution:
JULIA already has a: default REPL mode, a Shell mode, a package mode, … ? Help-mode … , why not having the best of both worlds in Julia with a default REPL mode → julia> plus a quick prototype mode that enables copy and paste from forums, copy-paste module code etc… Even a copy-paste from a julia> mode would not collide with the proto> mode in case ist contains declarations of globals within for loops etc. cause in proto> mode the declaration of a global within code would be redundant, but the proto> mode code wouldn’t run in the default Julia> Repl without throwing an error.
But…, i have no clue wether this “best of both worlds” a Julia 1.0 mode next to a old-school proto> 0.6 mode within the REPL is doable, or even disruptive.

1 Like

That’s certainly possible. The main issue is that people will then be confused about why their code works one way in one place and differently in another.

4 Likes