I actually second that. While I’m not particularly bothered by the current scoping rules (or the proposed new ones, it’s just a matter of getting used to them), I’ve never understood the need of having for or while loops creating their own local scopes (while, for example, it is pretty obvious for functions…)
It is true, though, that if for loops didn’t create their own scopes, all the complaints about unexpected scoping behaviour would go away…
I’m not a computer scientist, so I’m probably missing something here.
Yes, I get that. But do we need a whole scope just to protect for-loop counters from accidentally modifying other variables? There must be other solutions…
(and that, by the way, doesn’t apply to while loops)
I don’t know - I think it would be weird if that loop behaved differently if i was defined first. And I much prefer julia’s treatment of this situation to python’s. Not sure if a different scope is the only solution, but it has the benefit of being clear and consistent, at least to me (and I’m not a computer scientist).
What else would you do? For loops either introduce a scope or they don’t. There’s no such thing as half a scope. If you want loop variables to be local to for loops then there has to be a scope for them to be local to.
It’s easy to look at a short for loop with an assignment and think “Of course that should write to an outer variable, what else would I have meant by that?” But in real world, non-toy programs it’s quite common for a large amount of code to exist inside a loop body, with assignments at the top to variables that are used further down and never used outside of the loop body. In those cases you don’t want the variables that are only used inside the loop to hang around afterwards.
In general there’s no perfect solution to this because people want to do different things at different times. Forcing people to be explicit about it all the time is one approach—requiring var to declare new variables, for example—but that’s annoying all of the time. Otherwise you have rules that try to “do the right thing” most of the time. The best you can do is have rules that are right most of the time, fairly simple to understand, and favor better programming practices over worse ones. In general local is better than global and statically predictable is better than unpredictable, so that’s what Julia’s scope rules do.
Julia’s for loop scope behavior is also carefully designed so that the difference between for loops and comprehensions is minimal. You probably haven’t noticed because there’s nothing to complain about—because they work the same way. But I can assure you from experience with earlier versions of the langauge where they did not work the same that if they didn’t then everyone would be complaining about that instead.
Or… suggest that it is better to make it explicit that one is using global variables? Which is precisely what the scope change enforces now. When using julia, because of how it works, it is a good thing to be aware of global variables. I don’t think this is something we should try “hiding” from the user.
And, com’on, of course I am not advocating for people not to use Julia interactively.
Most of my usage of Juia is for interactive exploration.
This should be clear, but this scoping change does not prevent anyone from using julia interactively.
It doesn’t even make it harder to use it interactively. It is only annoying for the moment because it is a change from previous behaviour and we need to readapt our “interactive” coding habits.
We also have thousands of outdated online tutorials out there not helping us out with this issue…
In my opinion, the only true difficult that this change creates is for teaching Julia to beginners to programing. And for that case SoftScope.jl solves the problem.
PS: @carstenbauer, I quoted you, but please, don’t take this post “personally”. I just decided to voice more often my preference of the new behaviour because I feel that those on “this side” simply aren’t commenting much, thus creating an impression that most of users are against the new scope behaviour.
PS2: It seems that some people think that there is an opposition between “writing functions” and “working interactively”. I don’t think that is true. You can work interactively by writing small functions, and changing those functions interactively as necessary.
Fair enough. As I said, I’m not complaining about the current scoping situation. I was genuinely curious about the reason behind having a scope-generating loop, as it (naively) seemed to me that all these complaints could be easily solved by having for-loops behaving like if structures.
I also do like the fact that I don’t need to worry about is and js in for-loops.
I’d put myself in the “programmer” group of julia users. Explicit variable declarations appeal to me. As I’ve come to learn Julia has a large percentage of users here for the numerical computing who aren’t primarily “programmers”. They would tend to find var everywhere annoying.
I and hopefully other programmers would appreciate the ability to optionally litter our code with var or let. Maybe even have a “strict mode” which requires it.
In practice I think I’m asking for a variation on the let statement which implicitly inserts end at the moment the current scope is exited.
julia> var t = 0
julia> for i = 1:10
t = i
end
julia> t
10
julia> end # I want to explicitly close my var block to reduce REPL state
julia> t
ERROR: UndefVarError: t not defined
function foo()
var t = 10
var x = 1
var y = "hey"
....
end # close function. doesn't complain because of implicitly inserted end's
I don’t find explicit var to be even remotely annoying, and in fact far prefer it. It makes variable scope much easier to reason about and removes any ambiguity about whether assignments are modifying a variable declared earlier (which may have non-obvious consequences), or declaring a new variable.
To avoid the problem of an overly stateful REPL session, I’d want := or whatever paired with a way to either explicitly/implicitly make a var binding go away or the ability to switch between scopes.
Someone in the other thread suggested allowing a repl mode representing a new scope being introduced. Which could be a solution, a little unconventional though.
julia> x := 5
julia:{ x }> y := 10
# do these nest or does := mean assign to current scope?
julia:{ x, y }> popscope() # ? endlet? undef x?
julia:{ x }>
user=> (def x 1)
#'user/x
user=> (ns hey)
nil
hey=> x
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(null:0:0)
hey=> (def x 2)
#'hey/x
hey=> x
2
hey=> (ns hey/there)
nil
hey/there=> x
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(null:0:0)
hey/there=> hey/x
2
hey/there=> (ns other)
nil
other=> x
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(null:0:0)
other=> hey/x
2
[Edit]: wanted to add one more solution
technology could be a cure here. The problem with introducing a scoping block in the repl is that things become non-interactive until you end that block. There are those fancy code editors that show you your current values as you type your code. I use vim now, but I was using Lighttable for awhile and I believe I’ve seen it in Atom, here: hydrogen
If the default repl could do that, this could resolve the issue of managing complex repl state.