Thank you for being so generous with your time working on this project. Recently I was reinventing the trace of the Moon that leaves when it spins around the Earth, which spins around the Sun, which spins around the center of the Milky Way and so on... and the functionality of attaching one sprite to another was helpful.
To the point. I opened an old project and it didn't function. The problem was that I had nested for loops with the same variable for the counter. This project shows the issue Snap! Build Your Own Blocks
It took me some time in debugging. It was nice activity but I imagine it could create some confusion. Warning or auto chaning the name of the variable may help.
Did you know that you can debug variable scope in Snap! by turning visible stepping on? Then, when you hover over a variable declaration it highlights all the blocks that have access to the declared variable. And if you hover over a variable accessor block it highlights the block where this variable gets declared.
Check out this screen recording of debugging your nested for loop:
By hovering over the outer FOR loop's "i" variable declaration you can see that it eclipses the inner one. And the hovering over either of the "i" variable getters in your "WRITE" block you can see that they both access the same declaration of the inner FOR loop.
If the scope of the iterator (, in this case) were limited to code inside the loop, (accidental) use of the same name for iterators of nested loops would be less of an issue. Is that feasible?
I don't think it has ever worked. I think they're just saying that they created a script, not realizing they were using the same variable name for the inner and outer loop, and when they tested it, it took them a while to figure out the issue (because there wouldn't be error messages). At least that's what I'm assuming.
@jens Thanks for the debugging variable scopes demo.
@bh Not sure how, but I'm pretty sure it was working. I was using the project to make public demos.
This is the project Snap! Build Your Own Blocks . If you put a word in cyrillic, it will print on the screen adjectives in Bulgarian. One for each letter of the word, starting with that latter.
In particular this code was working fine:
Few days ago I opened the project and it was printing only the first word from the 'characteristic' list. When I changed the variable name from 'i' to 'j' it is working fine again.
The root cause of the issue is that Snap! unifies the outer loop's and inner loop's because they bear the same name. This stems from what must have been a general design decison: that each variable, even if it is an upvar or created within an inner loop, is valid all the way down to the end of the scipt it is in, as illustrated below:
For comparison, a piece of Scheme code:
(let ((x 1) (y 3))
(let ((x 2)) (set! y x)) (list x y))
⇾ (1 2)
What we see here is that - in contrast with Snap! - both the outer and the inner let use x as a variable name, but these refer to separate variables.
My proposal for Snap! :
Any variable created inside a control structure will only be visible within the control structure itself;
The scope of upvars is configurable (either just inside the loop, or also for the loop’s caller, or for the entire script).
Given this script
The first "report" should return the value of the outer variable?
But the second case is an error, as a variable goes out of scope?
I seriously doubt if it's expected by an average user.
That's an interesting idea. But it would give rise to troublesome questions if you assemble a FOR loop and then drag it into another FOR loop. We'd have to guess which variable you meant by references to the previously-common name in the inner loop.
Alas, this is one of those things about which Jens and I vehemently disagree. He actually writes code that depends on Snap!'s current behavior. I suppose "configurable" is conceivable, but Jens generally doesn't like adding options because he worries that the code to test the configuration will slow down the loop. But I suppose the test could be made early, by "compiling" the desired behavior into the loop.
I have proposed a compromise scope rule in which ordinarily the upvar survives below the loop, but a second declaration of the same name creates its own variable. I forget why Jens doesn't like that. I fear that he'll be mad at me for forgetting. :~(
And when the new variable should disappear exposing the old one?
It will introduce way too many moving parts and it seems to be overengineering for no real benefits.What should be a scope boundary? The C-shaped input?
Right now, you can use perfect locally scoped variables as formal parameters to lambda.
So maybe "the low-hanging fruits" are predefined parameters for rings & C-shapes.
You can also craft locally scoped variables with the pinch of meta programming
The beauty of lexical scope is that you see what you get. Nesting two same-named variables gets you into trouble, so what? It's not Snap's job to assist foolish users from writing foolish stuff, especially not by fooling around with variable scope, which is the single hardest problem any CS learner encounters. Instead Snap offers a rich debugging experience that helps foolish users find out what's going on. Those are learning experiences. Just because some user makes an error does not mean we should change anything. Geez.
I absolutely agree. Snap should not be kind to users who use the same variable names in nested for loops, it should teach users to use unique variable names to help keep you sane (you have no idea how confusing it gets by using the same variable name for multiple things). Snap should instead make users try to be good programmers, and punish them for using the same variable name (by confusing them).
Oh, I don't think we should do anything to make sense of a script with nested FOR loops with the same variable name. There's no way to make sense out of that!
Here's my application. I'm trying to implement lambda calculus in a way that looks exactly like the textbook notation. So, I have this:
but I don't think it's so outrageous (let alone "foolish") for me to want Snap! to be able to provide the actual notation without extra syntax needed. We can almost do it!
Never; what makes this a compromise proposal is that it maintains Jens's criterion that the scope of a script variable is from its creation to the end of that script, but it also allows for a situation in which someone might want to reuse a variable name within that scope.
And how it differs from the current behavior, neglecting the "for" implementation details (unconditional incrementation of the upvar before checking the end condition).
Never mind nested FOR loops; we are all agreed that nested FOR loops with the same loop variable have no coherent meaning and we're not obliged to imagine one.