Another possible solution to the global scope debacle

How about creating new keywords for the proposed changes? E.g. for! affects global scope by default, while the behavior for the current (1.0) for is maintained.

To clarify…

Are you assigning a value to a variable?
   No:  this topic is irrelevant
   Yes: Are you assigning within a module?
      Yes: this topic is irrelevant
      No:  Are you assigning within a function or struct def?
         Yes:  this topic is irrelevant
         No:   Are you assigning within a let block?
            Yes: this topic is irrelevant
            No:  this topic is relevant

As I understand it, this topic applies to top-level assignment within a julia script or the REPL and to assignment within a top-level for or while loop.

As I understand it, this is relevant within modules and in lets. Modules have their globals. And whether let introduces or not a local scope remains to be settled.

The way I understand this (see Stefan’s response above), the new rules affect all “global” variables, whether defined at the REPL (module Main), or inside a nested module.

You know what? This proposal is starting to grow on me. It’s perhaps just a matter of changing my mental model of what constitutes a scope boundary.

Okay, so let’s have modules and functions as the only scope boundaries. But then I think I would prefer let blocks to be transparent (leaky) just like for (option (1) in @StefanKarpinski’s post). Additionally, I think I would prefer the binding i in for i = ... to remain local, just like in let i = .... There is a nice parallelism there, and it’s easy to remember.

There is also the nice parallelism that’s been discussed that wrapping any top-level for into a function doesn’t change the way it works, i.e.

x = 0
for i = 1:2
    x = i
end
x  # x == 2

works the same as in

function f()
    x = 0
    for i = 1:2
        x = i
    end
    x  # x == 2
end

So, is there any real shortcomings of @jeff.bezanson’s proposal (not just a matter of taste)? It does solve the soft/hard scope complexity that motivated #19324, and I cannot think of any real drawback now… :thinking:

(EDIT: … apart from the obvious “how the hell do we get this into 1.x now?”)

7 Likes

It sounds like you have come around to exactly @jeff.bezanson’s position :grin:. I was skeptical about this design initially, but after mulling it over for a while, it’s grown on me. Yes, local is a better default than global, but the more skilled user can easily either (a) do things inside functions or (b) sprinkle a local here and there. Putting the burden of knowledge on the more advanced user seems appropriate.

11 Likes

please see
my post asking about this
maybe we are talking about two different things

The problem I have with this is that functions are supposed to be self contained (and ideally small) units. Therefore it should be easy to tell what values the for block is capturing.

If a global for block automatically captures global state, a user can easily mess up global state without intending to.

To me it seems better to opt-in to dangerous behavior rather than to opt-out (this is apart from any annoying verbosity).

If working in the REPL has too much friction, I don’t see why we can’t just provide additional features to help mitigate that friction. I don’t deny that working in the REPL is an essential part of most Julia workflows. Perhaps there is a way to be both safe and ergonomic.

When there’s a choice, Julia generally has people opt into performance instead of opt out (@inbounds, @simd, @fastmath, type-unstable code just works, etc.). Requiring someone to local seems to follow that guideline. I think it’s both consistent throughout the language and is more generally just a good design principle.

4 Likes

So I don’t grok what is it if module contents be global.

Yes, this would apply inside of modules—modules are global.

“modules are global” is different from this?

module A
a = 5
end
module B
a = 55.5
end
using A
using B
Error both A and B provide a variable named `a` in global scope

I would argue that mutating global state is the advanced use case! But I digress. There has been a lot of fruitful discussion and I trust you guys to make the right choice.

1 Like

Derp - I think I got thrown by this:

Thinking that variables outside of functions would all become what @mauro3 called “global global.” But it makes sense from context, apologies for being dense.

I think interactive usage is a less advanced use case than scripting and package writing, and in interactive usage, globals are what threads a computation to the next.

1 Like

I’m convinced! I guess I was focused too much on package writing rather than interactive use.

1 Like

What we do should help and not hinder future realization of namespaces, api protocols, trait organization, …
[I’m not implying anything about the proprosal]

Each module has its own global namespace; that’s an orthogonal issue to this one.

Just to confirm that this would have no impact on closures defined at the global vs. within function scope. So, the following behavior in the current REPL or in jupyter

a = 5
f(x) = a + x
@show f(2)

vs. creating it in a function

function g(a)
  f(x) = a + x
  @show f(2)
end
g(5)

Would have the same behvaior (and performance) in the proposed solution compared to today?

Has anyone considered making the behavior for interactive REPL use optional? The presence of the option could be hinted at in the error message and the user could then choose which behavior to have.

2 Likes