Local State with Script Variables

Following on from Scratch forum discussion on the Snap Manual

I've had a careful think and this is where my brain is at at de-constructing what is going on :slight_smile:

We are defining a reporter, make a counter, that uses a local script variable count

But instead of returning a simple value, make a counter returns a mini-script.


and because we are returning something containing a reference to objects/variables/blocks (not sure which) that are within the user-defined reporter make a counter, this keeps the reporter alive?

If i'm OK so far, then my next step is to understand that because the reporter is only returning part of its script (the increment and report value bit), then that is why it keeps incrementing and not resetting itself to 0 + 1 on each invocation.

How am I doing?


Consider a variable to be created by the script variables block, and never deleted, just usually made inaccessible. Snap! actually removes them to not leak on everything.

You're doing great, expressing the concept of a closure (a local variable that has become unreachable except for a procedure that survived the function that created it) very precisely!

It's the variable that's kept alive because the script points to it. And the number is kept alive because the variable points to it.

The way you're supposed to think about it is that everything lives forever (which is different from being accessible everywhere -- that's not the case), but the system is permitted to reclaim memory if it can prove that nothing points to it.

And, yes, the SET COUNT TO 0 is evaluated when you call MAKE A COUNTER, but the stuff inside the ring is not evaluated then -- that's the whole point of rings, to contain code without running it. It's when you call the ring (which you do by putting something that points to it in a CALL block) that those inner instructions are evaluated.

It's really beautiful. The "object oriented" languages think they have to invent special language features to provide local persistent variables, but, as the Beatles sang, all you need is lambda. (And lexical scope, so that the variables that are visible inside a procedure are the ones that were visible when the procedure was created, not when the procedure is called.)

I've come up with another view in my brain now :slight_smile:

The script is like a simple class in other languages - the 1st bit is the constructor or init method (create and initialise class variables)

Assigning counter1 to make a counter creates an instance of the class

The object only has one method that increments the count variable and returns the current value when its called

Then next section

shows how to have and call more than one method within the object

Although I can imagine I am mangling the beauty of lambda/LISP/Scheme :slight_smile: - is my simple viewpoint valid?

Hi Simon, perfect! In fact, I often use that exact explanation myself when introducing an audience to first-class functions and closures.

1 Like

I'm having a bit of practical difficulty in re-creating the example

Here is my make a counter

When I try and make the ask block - The ringify doesn't auto-magically disappear when I drop the inside call into the outside call and I can't seem to remove it (and therefore I can't get it to work)


Oh, you don't need to make an explicit ASK block, just call the function you get when evaluating MAKE COUNTER directly. That is, you can assign an "instance" of what you get from MAKE COUNTER to a variable, say, "counter 1", and then just call "counter 1" passing in the message and parameter as arguments.

No, Jens, calling the instance with a message reports a method, which must itself be called. It has to be this way to allow different methods with different numbers of inputs, without hairing up writing the methods themselves.

To remove the unwanted ring, right-click on the inner CALL block and choose "unringify" from the menu.

Simon, the default behavior of CALL with respect to rings does the right thing for almost all straightforward uses, but once you start rolling your own OOP you have to think about where you do and don't want rings. Which you did, but you didn't know the mechanism to get rid of a ring.

1 Like

I see it now :slight_smile:

Maybe add the how-to in there for the next person :slight_smile:

Nearly there....

but a reset produces a list


Is this a "mis-feature" :slight_smile: or have I declared my parameters wrongly somewhere

You have
but it should be
You get the "input list" variant by dropping the args block onto the arrowheads at the end of the CALL block. You'll know you're in the right place because you get a red halo over the whole inputs area.

Yippee - finally got there :slight_smile:

Moving back to the manual then..

  1. Don't call the reporter ask (as there's already a built in reporter called ask that does something different)

  2. Add in explanation of dropping lists onto the arrowheads after this on page 49

silly me - should have read further down :slight_smile:


OK - I'm starting to have ideas above my station now

In sizes example on pages 49/50

numbers is set to accept multiple inputs
Snap seems to automagically convert that to a list and processes it within the script as a list
But then later, we have to drop the next list onto the arrowheads so that sizes doesn't get upset that it is being passed a list

But since it treats an input list as a list, couldn't it automagically detect that its being passed a list rather than the programmer having to explicitly having use the dropping onto arrowheads.

Or is the overhead too much or is there something further down the rabbit hole that needs this technique?
Or should I go back to working in 6502 machine code? :slight_smile:

Oh gosh I remember 6502 code. Not much fun.

Imagine that the variadic input is of type List. So inside the procedure, it looks like a list of lists. But outside the procedure, what happens when you drag a list into the first input slot? Is it a "regular" list, a single input, or is it a list of inputs?

So it's easier all around for us not to try to read the user's mind about this. Both recursion and variadic inputs are somewhat advanced techniques, so someone who's using both of them is old enough to read the manual! ;~)

Unringify the inner block!!!

It has already been solved.