Script variables and upvars: make 'm local

Summary
In my opinion ...

  1. The scope of script variables and upvars is unnecessarily wide for practical use.
  2. It goes against Snap!'s design philosophy.
  3. It is a source of unnecessary error.

Therefore I propose to limit the scope of both script variables and upvars to 'local'.

The scope of script variables and upvars is unnecessarily wide for practical use
Consider the following block:

Sorting script pic (5)
In this block "number" is a local counter, and its value is relevant inside the FOR-block only. Even so it exists inside the entire script that this FOR-block is a part of.


Edit: initially here was a parapraph about variables being available above the SCRIPT VARIABLES block in which they are declared. On closer inspection I found the latter to be false, and (since I don’t want to cause confusion) I removed the parapraph.


Another case:

Sorting script pic (10)
In this case, as well, there's no use for variable "store" to be available outside the IF-block. If there were, it should be declared at a higher hierarchical level within the code / environment.

It goes against Snap!'s design philosophy
From what I have understood Snap!'s design philosophy is mostly a mix of functional and OO paradigma's. Both are characterized by implementation abstraction, the latter implying local data wherever possible.
I've been doing some reading in the venerated SICP :wink: , and I'm sure the authors would agree. Admittedly this is an argumentum ab auctoritate (which Wikipedia calls a fallacy) - and even worse, I invented it - but I guess you catch the drift.

It is a source of unnecessary error
Suppose within the environment of the aforementioned FOR-block a (global, sprite or script) variable called "number" already existed, like in the following example:

Sorting script pic (8)
Guess what it reports ... 11.
If the variable "number" is going to be used again somewhere down the script, the programmer will probably have assumed that its value is still 123 (or whatever it was after the "various operations and stuff"), which in reality it isn't: an unnecessary source of error.

Therefore I propose to limit the scope of both script variables and upvars to 'local'.
That is: a variable will be avaliable only within the scope of the structure it is declared in.

Is it an urgent and necessary change? No. But it would be a wise change (I think).

First, this change would break a lot, and I mean a lot, of projects. Second, there are actual good uses for using an upvar below the block that creates it. For example, an upvar can create a variable with some special value, like a block, then the scripts underneeth can use that variable to do more stuff with it.

Another thing. If you put a variable in a ring, it keeps it's context, no matter where the ring goes. If a script var in a ring is sent to another sprite, that sprite can change the variable and it would change in the original script it was created in.

Oh, and it would confuse a lot of people, more than script variables do now.

Overall, ihis is an unneccicary change that makes no sense as to why they would change.

As for this,

The user (or at least new users who know very little about programming) would actually expect the value to be 10.

Upvars are meant to be usable both within the block itself (as with for) and in the rest of the script (as with e.g. a custom let block or any other block that reports values through its upvars). This makes the latter impossible and introduces inconsistency between script variables and procedure upvars... I don't see this change getting implemented.

EDIT: Variables being accessible outside the if/repeat conditional primitives is a bug; the primitives don't create a new scope for the blocks within them. If you define a variable in a custom conditional it behaves as you expect.

There was in depth discussion of this almost 2 years ago after the first few posts in
https://forum.snap.berkeley.edu/t/what-is-an-upvar/5387/15

I'm sorry, but in my opinion this is a horrible idea. Do you know how many snap projects this would break? I'm not sure if I even read this correctly, though I've read it twice.

I could give a lot of reasons why this is not wise, but I don't really want to.

Could you provide an example of a project (existing) that would break if upvars were limited in scope

Lets say you have a for (item) in (list) block. If the (item) is only limited inside the for block, that would be disastrous to many snap projects, most likely including your own.
I know it would affect mine.

i don't feel like that describes the problem well. what actual code would break? i can't tell if you're misunderstanding the issue or have an actual code snippet, and i don't think they can either.

As for @ego-lay_atman-bay ’s criticism: you have a point, upvars as they are now do have applications - I should have thought of that myself, from the discussion on topic: Scheme's LET. However, most of the time (e.g. with conditional and iterative blocks) variables being available outside the scope in which they are defined is useless (and, as I argued, a potential source of unnecessary error).

So let me, pragmatically, amend my proposal:

  1. Additional to single input, multiple input and upvar, let there be a fourth option in the long input name dialog box: local variable. This local variable exists only within the same command / reporter predicate block, including any defined inline commands, reporters, predicates and anything inside a C-shape. So “upvar” remains an option. The difference between an upvar and a local variable (locvar?) is that the latter does not exist outside the structure in which it is declared.
  2. Conditional and iterative primitives, such as IF and FOR will use local vars (I really don’t think upvar should remain an option here; utilizing their data outside their structure is probably bad coding - unless anyone has an example to the contrary?)
  3. Other than upvars, any variable will be available only within the scope of the structure it is declared in.

Thus I believe well thought out existing code will hardly be affected - and if it is, can easily be repaired. But I may be wrong. Happy to learn from criticism, and sharpen my thoughts, and proposal.

i think the upvars should be part of the c shape and called parameters, since custom block Cs are just dressed up rings. a block might also have multiple seperate c shapes that shouldn't share variables, such as a FOR ELSE block, which iterates over a list, or runs the else if the list is empty. this also means that rings dropped in the C can use those parameters (technically right now C shapes can be called with parameters in the block, but why should blocks contain secrets?)

Ok, I just want to say, your idea that upvars on blocks, such as the for loop, shouldn't be accessible outside the block, is not very good, since almost every single programming language does what snap does. Here's an example in javascript.

let number = 123;
for (number = 0; number <= 10; number++) {
  console.log(number)
}
console.log(number)
// result is 11

In this example, we set number to 123, then make a for loop that goes from 0 to 10, using the number variable. This sets the variable outside the loop to 0, then loops, adding 1 to it, until it is greater then 10. If you get the number variable outside the loop, it then returns 11, not 123.

Now I do have to mention this. In my example, while I was proof checking it, I ran into this.

let number = 123;
for (let number = 0; number <= 10; number++) {
  console.log(number)
}
console.log(number)
// result is 123

If you specify let in the for loop, it creates it's own scope, which is what you're asking for.

I did also test it out with both instances of let being var instead, and that actually had the same result as the first example.

I think snap falls under the first example because it does not show that the variable is being created in a new scope, so a user would expect it to be using the existing variable, like when I tested the javascrript code with var instead of let.

in nearly all situations defines would be done in the for loop, like the second example. defining the variable before the loop than using it as the counter is rare and confusing, and defining it in the loop, but having it accessible outside the loop doesn't happen in any language.

snap variable scoping also doesn't work like any common programming languages.
try to guess what this script does before running it:
untitled script pic
note that when variables are defined but not set, they're 0
i guessed (outer, inner, outer), (outer, outer, inner, inner)

Are you running out of variable names???
Dont name variables to the same name!

So … LET, Scheme’s way to declare (and initiate) local variables, is supported by Javascript (and mostly preferred over VAR these days, I read, precisely because of its lack of - error-prone - side-effects) but not by Snap!, which is to a high degree Scheme-based?

Yes, I'll second that... but a sub-scope can be explicitly introduced with a ring.

let be.3 script pic (2)

Using a thin (literally :smile:) wrapper
let be.3 script pic (3)

let be.3 script pic (4)

What my proposal - the original version even more so - is all about: that Snap!, being primarily an CS-educational environment, behaves in a manner that is intuitive for novices and, at the same time, stimulates their development of good coding practice. What @dardoro demonstrated in post 15 is really clever, but it’s not something novices will do, or even teachers will want to teach them in an introductory CS course.

I think of Snap! as a bit of a "woolly" language at times :slight_smile:

It's very forgiving, it lets you make mistooks and tries to carry on as best as it can.

Which is good for early learners

Now some parts of Snap! are a bit more rigid of course, but I think something like the upvar leaking out of it's C-block isn't a major cause for concern.

In practice, I don't think it's likely to cause an issue when teaching.

I agree. As I wrote in my original post:

So perhaps it’s something that the Snap! team may want to consider including in a future release. I’ll leave it at that for now.