Just to clarify: I think of the combination of nontrivial control flow (for, while, etc) and global state as something that should be possible and has some uses, but I don’t think the language should encourage it as it runs into other issues (mainly performance traps, which are at least equally confusing for newbies).
“Sugar-coating” here refers to the strategy of making a certain kind of coding style easier for newbies, which then, if adopted, runs into inevitable problems later on. I understand that the intention is to help newbies, but I just don’t think it does. This is why I care about this, even though — you are correct — personally I am not much affected as I avoid this kind of code implicitly.
Also, I don’t think that the cost of the current rules is too high, either. It is my impression that people just bump into this once and learn about it, like many other things. But again, it is possible for reasonable people to disagree about this, as we don’t have hard data.
That said, this issue is not black and white for me. I recognize your POV as valid, too, I think we just have a different weight on certain things. Also, I trust the core devs to make the right choice about this eventually. If they adopt a “friendly” version of global scope for scripting that is consistent and easy to reason about, I am fine with that.
We do, however, have the experience of every other dynamic language that is widely used interactively, and none of them behave the way Julia does with respect to scoping of global loops.
(Julia’s design has always been to make as few sacrifices to usability/productivity as possible, compared to other dynamic languages, in order to allow high performance. Not to make sacrifices to try to enforce high performance.)
It’s not just “newbies”, it’s also experienced programmers who want to explore interactively some of the time. Not all code is performance-critical, nor should all code be optimized for performance!
What it comes down to is that the main “benefit” people are citing for the global scoping rules is that it makes commonplace interactive (but slow) code harder to write, not that it makes life significantly easier for “serious” coding. I don’t see this as a compelling argument.
We can’t change the rules now, but we can still improve the interactive situation.
@Tamas_Papp I object to your always repeated assertion that only newbies would ever write loops at global scope. To give some examples:
@benchmark always demands top-level scope (no function!). Hence, lots of ugly annoying global annotations if you want to fill a table with a few 100 benchmark results. Generating + plotting + archiving some data comparing different algorithmic choices and scaling behavior is imo good practice and not “just for newbies”.
Batch processing a bunch of problem instances over night. Of course the functionality is inside proper functions and compiled. But in the end, there is some code-vomit-style script that calls all these optimized functions. Sure you could work to write beautiful scripts, but time is better spent writing beautiful algorithms.
Interactive exploration of data / problems. That’s what jupyter notebooks are super cool for! You want no scoping problems here, and don’t care about perf (because there always is a function boundary before hitting expensive stuff).
Most of my code and workflow is unaffected by global scope questions. But there are literally zero cases where I as a user think “oh, how nice that we have this new scope rule, so much better than 0.6”; instead, it is a constant low-level gripe.
@jlperla gave a perfect explanation of the mental model many people: Top-level code has an enclosing scope, just as every other piece of code, and the same rules apply (for top-level code, the enclosing scope happens to coincide with module-level global scope). This is a perfectly sensible mental model and language design. Julia does something else (also perfectly sensible), where the global-local dichotomy is more pronounced than a “hierarchy of scopes”. I think that everyone here should try to understand both models, not just the one currently chosen in julia.
I think the thread is well-intentioned, but there is a much deeper issue here. The basic consensus among people who do lots of scripting seems to be: (1) Tell beginners, who invariably write script-style code, to start in IJulia; (2) avoid the REPL or julia files for scripting-style entirely and only work with non-script code (i.e. everything in functions). If you tell them that code at the top level simply won’t match their intuition, it will save them confusion until they understand Julia well enough that this isn’t a problem.
I think that making the “tips” more complicated will likely confuse them further. I think having a single, simple tip is the easiest. Explaining a let to someone who doesn’t understand the subtlety of scoping, isn’t going to help.
Please don’t put words in my mouth, I never said this. The pedagogical motivation is prominent for soft global scope though, which is why it was repeatedly used as an argument in favor of it (and I think it is one of the best arguments), and why I used it as an example. This does not mean other uses are not relevant.
I am now going to withdraw from this discussion for a while since I am not comfortable with its tone any more. I understand that there are people who feel passionately about the issue, but I consider it a technical one best discussed in a dispassionate manner. I am afraid that this is becoming very difficult now.
I agree that the language would preferably not encourage the use of globals. (Personally I would have preferred all variables to be local, even in the repl or a script, unless explicitly declared global, but no breaking changes allowed …)
I still advocate the use of SoftGlobalScope in the REPL because of the following reasons:
I am convinced that most of the people using things like for loops in the repl do not intend to create and use global variables. Speaking for myself, I have no problem with hard scoping rules in scripts, because I put everything in functions anyway. I do have problems with it in the REPL though: The main advantage of a REPL for me is to be able to interactively test, debug and experiment with code (written in the functions). That copy-pasting code from functions to test creates globals in the REPL is an unfortunate by-effect. The hard scoping rule makes this way of working hard (no longer possible to just copy paste code from functions). To give at least one hard data point, I consider this a very high cost: if not for the softglobalscope solution this might have caused me to abandon further exploring Julia (even though I really like a lot of it).
Interactive use on a repl is also a good way for teaching newbies (in my experience). With soft scope, they can try out loops etc. in the repl and learn about scope when they get at functions, where it is relatively easy to explain. Then you teach them about the performance implications of functions, and tell them to wrap everything into a function. With hard scoping this gets all muddled, having to talk about scoping too soon, and having the simple stuf they did interactively (with the globals in for loops) not work anymore when they wrap it in a function.
For evangelizing about Julia (i.e pitch it to users of other languages), the hard scope also presents a problem. It is different from most (probably all) other languages. There is nothing wrong with being different from others, but you must be able to defend it, to explain why this different behaviour is a good thing. e.g. I’ll happily explain multiple dispatch and its advantages to someone, but I fail to see the advantages to the programmer of hard-scope in this context.
If I had to teach Julia, I’d make sure they get access to a REPL with SoftGlobalScope, and tell them that if they want to run code in a file, they must always put it in a function.
I would like SoftGlobalScope to be the default in the REPL (as it is in IJulia), or at least very easy to use (e.g. command line option).
For people (like me) it might be helpful to go for the c/c++ style: if you want to run it “standalone”, it is your main() program. I wrap my script into:
using CoolStuff
include(“important_code.jl”)
function main() #your script here
end
main()