Lambda unbinding, getting bindings, script bindings

EDIT: condensed the most important and relevant bits since it got confusing, and many proposals changed

an "object" here is a stage, sprite, or procedure. they all have variables, can be put in the OF block, and be told to do things with TELL and ASK. bh says this naming is correct.

  1. change the OBJECT [ v] block to return procedures put in. this is extremely unlikely to break projects and is more consistent with actual groupings. either that, or change the name to something that means "sprite or stage" (although i'm not sure what that would be called)

  2. a properly supported way to create a procedure or other object with no variables (not even globals). currently you can do this with JOIN procedure, but the behavior will apparently change later to include globals. libraries can't decide globals or sprite variables, so it's most important for them, and it can't just be ignored due to the [variables v] OF block.
    i suggest making it possible with (procedure) OF [none v] and OF [global v] where none has no variables, and global only includes globals.

  3. i'm not very sure about this suggestion now (it would probably be difficult to implement), but it would also be useful to have a way to inherit variables from multiple places. it's possible to inherit variables from one place and create extra script variables (use the context of one procedure, create some script variables, and return another one), but there isn't a way of inheriting from two unrelated places. copying the variables doesn't share them like inheriting does.

original post (mostly), has some worse suggestions related somewhat to [this](https://forum.snap.berkeley.edu/t/variable-scoping-issues-on-joined-blocks/12390), but with more edge cases. sorry this is probably too long not sure if these are possible or not, but i'm pretty sure they aren't or would be extremely weird hacks

i can't find a nice way to unbind a lambda, running ((lambda) of [ v]) just returns nothing, when i would expect it to give a lambda with no variables attached. splitting and joining would work, but i assume that has a large overhead, and joining by itself messes up the function.

EDIT: ((lambda) of (join (RING WITH SOMETHING IN IT))) works, but is inelegant. i think ((lambda) of [ v]) should work


i can get the variable names and values from a function, but not a scope that can be placed onto other functions.
if there isn't a way to get it, i would suggest ([sprite v] of (lambda)), ([environment v] of (lambda)). and/or ((lambda) of (lambda)).
note that script local variables come with a lambda environment, so sprite might be slightly misleading unless it removes them. ((lambda) of (lambda)) feels the most logical and i think would hint better that empty lambdas could be used as variable environments.

note that this isn't about getting the current values of the variables, that can be done with (([variables v] of (lambda)) of (lambda)), the problem is that it doesn't share the variables (globals become local and such)

EDIT: this actually can be done with ((lambda) of (lambda)), i forgot to test it because it's the last possibility i thought of. i think all this needs now is documentation.
note that you can also tell a lambda to do something with the tell block


i don't see any way to append to an existing environment, the closest thing is inherit, which (i assume) has other related effects, would be very confusing to use for this purpose. i think there should be an "inherit [ v] from [ v]" block, possibly even to replace the current one (since selecting a specific sprite is easier to understand just by reading), and going off the "just by reading", it could be even more understandable as "use variable [ v] of [ v]".
a major issue with this block is that it can't actually look like what i described, since it would need to:

  • show a dropdown of relevant variables like the ([] of []) block
  • take in variable inputs like ([variables v] of (thing)) (the ([] of []) block is able to do this)
  • show upvars for the input(s) given
    this is the hard one! it's not even possible all of the time and would need to automatically adjust to the variable input when it can.

this is a bit of a design challenge, it's too late for me to think about this right now, and this post is already too long, so i'll stop here. and yes, i did find real use cases for all of these.

I'm not understanding what you mean by "unbinding." Do you mean that you want the lambda but with no formal parameters?

I think you should use SPLIT on the lambda expression and then play with the resulting list structure. That's what I'd try, anyway.

As for INHERIT, it means to inherit something from your parent sprite. That's complicated enough without bringing other sprites in. If you want something to be available to any sprite that wants to use it, just make it global!

Let me know if I'm misunderstanding what you're after.

jens referred to functions with an environment as bound, what i'm suggesting is a faster way to take a function and remove all variables it's related to, so it can't access any sprite variables or sprite properties like position. it's the same as you would get by just split and join, but without having to split it up in the first place.

custom blocks can't just make arbitrary variables around them global, names would collide and things would break. what you said would even apply to the current inherit, and i assume that block wouldn't have been added if it wasn't considered useful.

i think inherit is made much MORE complicated by having a weird tie to a parent sprite, it's not obvious what it does and the use cases feel more limited than they are. i think "share this variable" makes more sense.
there would likely be some complaints about ruining the locality of scopes and such but that's already quite ruined enough with ([variable] of [sprite]) and ((lambda) of [sprite]), everything except script variables can already be accessed from anywhere.

Prior to the metaprogramming features, we didn't really have to talk about environments because making a ringed procedure automatically attached the right environment and everything just worked without the user worrying about how it worked. But now, with SPLIT, you can separate the code from the environment in which it was defined, so that later you can attach that code to a different environment if you want.

i think this post is relevant, what i'm essentially proposing is to slightly extend existing blocks so that lambdas could be first class environments, able to capture sprite properties, globals, locals, and script variables.

EDIT: see edit on first post, they already can be used as these.

OK, I'm going to declare this topic finished, because you can do most of what you want with TELL and the rest with SPLIT. Although I should tell you that we're probably going to change SPLIT so that the resulting lambda does have access to the global variables.

i have a project i'm working on right now where a lambda needs to NOT have access to global variables, so if that change is made, i would need something that removes those, like ((lambda) of [ v])

i also really need the arbitary inheriting i mentioned for quite a few different reasons

I'm gonna have to see a project to understand your needs.

that's the dictionary/object project you've already seen, which carries over to the various places i would use it
i would also likely much prefer scripts from having access to global variables in some cases for libraries that need to sandbox scripts in certain ways

i remember i need the inheriting for quite a few features of the dictionary/object project, and there were other things but i can't quite remember them at the moment.

i don't know how the internals of snap handle "script scoping" (it shouldn't be called that) but it does allow each environment to inherit everything from another by adding to it (object with assoc block in the object/dictionary project), and sprites can choose to inherit whatever properties they want, so i get the feeling that inheriting from whatever should be possible without redesigning how variables work

Okay, I hereby declare this discussion as going over my head. We'll have to wait for Jens.

I'm reading along, but this goes over my head as well. I think I understand what they want but I don't see any good reason for wanting it except to prove that they must be really smart hackers.

the discussion went all over the place, and i changed some ideas, so i'll try to restate things and give some context:
ring, function, and lambda are all words for the same thing here

almost every ring has variables it can access, the same as the script it was created in.
when a ring is created with JOIN, it can't access any variables, and any rings created in that can, similarly, still not access any variables.

any existing ring can use the environment of a different ring by using the OF block ((A) of (B)) or the TELL block (TELL (B) TO (A))


((ring) of [ v]) gives no value, but it would be nicer if it gave a ring with no variables in its environment.
it would be nice for the TELL and ASK blocks in the same way: tell [ v] to (ring)

this is just a shorthand, and to be clear it shouldn't capture ANYTHING, not even globals. ([variables v] of (ring)) should be an empty list.


currently the INHERIT block only allows sharing variables from a parent sprite. rings can share variables by being created in other rings, including with the ASK block, like this:

untitled script pic

note that setting these shared variable changes it in both places, it's the same variable.

variables values can already be asked for with the sensing OF block, but it could also be useful to inherit them, with a block like INHERIT [] FROM [(sprite list) v], where a ring could also be dropped into the sprite list, just like the sensing OF block.

something to note for this block is that with it, there's no way to get the actual variable, so i think the INHERIT [] FROM [(sprite list) v] design should be in the variable getters/setters library, and the default block should be a INHERIT ((upvar)) <> FROM [(sprite list) v], where it uses a variadic upvar just like the SCRIPT VARIABLES block.

Ah, now I have some idea what you're after.

I think that the golden rule of lexical scope is that procedures created in an environment remember that environment. Probably, if we get second thoughts, that should include ones created with DEFINE. Yes, that means that the environment remembered by the block is different from the one in which its name is bound, but that's already the case.

I seem to recall that Jens had some reason why that'd be hard to implement. But we can certainly give the DEFINEd procedure access to the global environment, and so we should, as a possibly-necessary compromise.

Basically, I don't see why it's desirable to restrict the set of variables reachable by a procedure. If the procedure wants to reuse a name locally, it can do so.

Next, what about an empty ring? That doesn't mean anything in Scheme, so we are free to do whatever we want with it. Our choice is to interpret the empty ring as an input slot, and so we substitute the actual argument value into it. This allows, for example, using (MAP () MY_LIST) to implement the identity function, but ensuring that the spine of the result is different from the spine of MY_LIST.

Now for inheritance. (I know it must seem that I'm lining up your ideas just to shoot them down, but we have thought deeply about these issues!) A relatively minor issue is that in real life you inherit from your parents, not from some random stranger. When in doubt, it's best to keep an analogy as a precise mapping of the thing in the computer. So if we wanted to do what you want, we would make a new block whose name isn't INHERIT.

More subtly, a procedure shouldn't have to know who its parent is, and it might even be different in different contexts. This is the idea of polymorphism. The classic example is that you can define Number:AREA to be (* SELF SELF) in the class Number, and then any object that has a multiplication method automatically gets a square method, but it's a different method if the number is an integer from if the number is an exact rational or a complex number or, for that matter, an n-by-n matrix.

Sorry if this is stuff you know already. It's hard to know where to pitch an answer here with our huge range of ages. Not like in a school, where all the kids are the same age ± a year.

We already talked about this. Ring and lambda are definitely the same thing. Procedures are what you get when you call a ring. I try to avoid saying "function" unless I'm talking about a genuine mathematical function. (That doesn't mean it has to be about numbers, just that ∀a,b, f(a) ≠ f(b) ➞ a ≠ b.)

P.S. As to the same variable having different bindings to it in different environments, we already do that; that's what an Upvar is!

i'm fine with most of what's said there, but the main point here:

mostly this:
variables of
currently i've put the stage, sprites, and rings into the group "objects" (even though that's not what you or probably any of the devs mean by it) because they all have their own properties.
currently rings are the most flexible because you can always start with no properties, create new rings, inherit from previous rings, set values, etc
there's no way to create empty sprites, and the current codification setup is the only way to get something that doesn't inherit globals. many things i'm working on already rely on it

if it were just for my projects i could probably make some empty sprite and never use globals, but i often make libraries, where you can't guarantee anything about how the project is already set up, or do certain things with user code, where it's useful to be able to control it to some extent

my main reason to use snap is to encourage and help other people to create, if it was just for me i'd probably be using some "serious grown up language" like rust that has a massive barrier to entry. i like to mainly create projects that encourage other people to code (or just push snap to its limits but that goes well with this too)

i don't think this is quite as related but i remember at one point in a project i made a clone of a sprite and ran regular scripts on it to track what it WOULD do without actually doing those things on the main sprite. this probably wouldn't be helped by unbinding but it's a situation i found where being able to contain variables makes coding a lot easier.

something i also personally like a lot about it is that it makes pretty intuitive first class dictionaries. if someone can understand getting values from a sprite, they can make the step from there to rings, and then rings just become dictionaries with far better performance than an assoc (iirc) and has all the cool features of it being an environment like being able to just use all the properties directly as variables (since you can inherit the environment)

Actuallly, to the devs they are objects, because Smalltalk! It's only when talking to users that some of us don't say so.

oh good so i can use that word without getting complaints
although if that's what object means i think the OBJECT block should probably be renamed to SPRITE (or be changed to just return procedures, it probably wouldn't break anything especially considering it works in most situations a sprite would go in anyways)

It made perfect sense to call it OBJECTS back when the only user-visible objects were sprites -- and the stage! It's not a sprite but it definitely is an object; you can TELL it to do things, etc. lIt's because of the Stage that we said OBJECTS.

i feel like there really needs to be some word to describe both sprites and the stage, but i can't think of anything good, just "spritelike" and "view object"

i tried checking help dialogues, but they either avoid it or say "sprite" even when it includes the stage (SET BALANCE says sprite)

also while browsing i found you can hide the stage for some reason...
thinking about it a lot of things would make more sense if there was no stage or pen layer, just a background color and drawing on sprites
maybe i would make a fork like that if i could actually wrap my head around the codebase properly

It would break the idea of the stage as background. Sprites are always in front of the stage, for example, even if you tell a sprite to GO TO BACK LAYER.

i'm not actually suggesting snap should do that, it would break nearly every project, it was just an idea
also i don't mean "stage layer", i mean the stage. there wouldn't be a stage as background if there's no stage.

anyways this is pretty off topic and doesn't really matter so i don't think we should continue discussing this here, i'll just make a few edits to the original post to clarify things since the thread's a bit long and confusing

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.