Objects in Snap (without js...)

@bh i think ive crossed the threshold...

check out what ive been working on, theres lots of good stuff im quite pleased with

property / method getter : untitled script pic(62)

property / method setter : untitled script pic(57)

call method : untitled script pic(63)

run method : untitled script pic(59)

constructor : untitled script pic(60)

this-bound setter : untitled script pic(61)

lots of the same functionality from before but now i think im finally starting to get the hang of closures and local state variables, and i think i may be able to get some sort of prototypical inheritance,

still i think these could be packaged up all nice and neat but for now im gonna continue down this rabbit hole

Wow, pretty cool!

I have a few suggestions:

  • You can use the find first item list block to implement the get operation. You can also use it similarly for the set operation.
  • You could take the challenge of implementing objects via a hash table instead of an associative array, like how JS does. Hash tables are kinda tricky though. But it’d be cool if it was pulled off.

I couldn’t look more into this project since I’m on a phone. It’s hard to navigate the UI on such a small screen. My browser also likes to freeze and turn black when I try doing anything on the site.

Oh did you use new Object for object creation? I think I looked into the wrong block.

heres another example of what i got now

(ahem @jens this is a prime example for why variables should be named / created in script)

and yes this block

untitled script pic(74)

creates an empty object which you can assign to a variable (can be local) and use that variable with the custom blocks.

the Object block,

is just another class, still created with the new object block. its meant to operate on other objects, rather be used with the custom blocks. the blocks effectively remove the need for this one.

to your points,

ive tried the find first block and too thought itd be more efficient (especially with that compile option), but i ran some tests and found that the whole (item 2 of item index of key in item 1 of columns of...) method is (surprisingly) significantly quicker then using that check block. i think its because theres really only one check being done by snap (test if the column contains key) vs checking each individual row.

and hash tables ive been trying to look into lately among other methods but they are still quite alien to me. so till then im stuck with finding indices of keys in column 1

Neat. Although the class block could be a reporter, so instead of saying class me (this) { ... }, it would say me = class (this) { ... }. So not really a good use of the create variables library. (I believe that's only good for dynamic scoping or something, and for text to snap compilers written in snap, but there could be more use cases.)

I see how find first item in list would be slower than transposing the list and using index of.

Also I've been doing some research on hash tables and they don't seem too complicated. Hash tables are just an array. Each value stored is mapped to its hash. In case you don't know, a hash is a number that represents a unique object. It should look random, but it can't actually be random, since its result is entirely dependent on what you're hashing. So, for example, if you take the string "Hi" and its hash is 481234, the string would be stored in the array at item 481234.

That's how it works in theory. In practice, the capacity of hash tables can't be infinite, so when storing something they mod the hash by the array's capacity. Also they have to account for collisions (when multiple inputs generate the same hash), so what they do in the case of a collision, is they store all the items at the same index in a linked list, and when getting from the hash table, it uses find first item in list to get the correct object. Also they have to resize and rehash the array when the percentage of used slots (the load factor I believe it's called?) gets too high. I believe rehashing is a costly operation.

I'm not sure if you understand but at least I tried explaining. I wonder if it'd be possible to hash a list in Snap efficiently. I think what implementations usually do for arrays or objects is use their memory address, but you can't get that in JavaScript.

Great!

Yeah, this was my only major criticism of your code: to make a child object you copy all the properties of the parent. You want to be able to make 100 clones of some object, then change a method in the parent and have the change automatically appear in all the children because there's only one copy of the method.

"Look random" isn't really the point. I think you are mixing up two different uses of hashing. When you hash a string to keep it a secret, then it's important that the hash function not be invertable -- you shouldn't be able to look at the hash and figure out the originall text. But when you hash a string to enter it in a hash table, it'd be okay in principle, and maybe even in practice for some applications, to take the last three letters of the string as the hash value. (Last three rather than first three because if you're making a symbol table and you have variables foo1, foo2, foo3, etc., you don't want them all to end up in the same bucket.) What's more important is that the function be uniform: every hash value should occur with equal probability.

But the thing about hash tables is that, although they have near-miraculous constant-time lookup and insert, there's a large constant factor, so you have to have hundreds of entries before it's worth using a hash table. Much more common for smaller sizes (such as the fields and methods of an object) would be a (log n)-time balanced tree algorithm.

I'm curious how a tree data structure could be used to store data like that. I'm not too familiar with balanced tree structures. Please elaborate.

my problem with writing it that way is now i have this whole orange border around my code, and the definition is nested within a nested wrapper which seems very counterintuitive to me

where the real definition is hidden away in the custom c-block,

when i would like to just have a c-slot where i can define a function which is assigned to the variable. something like

untitled script pic(77)

then

untitled script pic(78)

and i wouldnt have to have 10000 dialog boxes open just to see what everything does.

thats just the way i think though. not saying its better or worse only my preference.

and i was following you until about a half - 2/3 of the way down, im still not too familiar with all the technical jargon that gets thrown around (thanks wikipedia, teaching me everything but what i need to know) thanks for trying though. the biggest thing is what you mentioned at the end, i dont see any practical way or reason, how and why is my questions lol(in that order)

You mean when I mentioned hashing lists? Now that I think about it, it probably won't be too slow. Strings are hashed by iterating through at least several characters of the string, like as an array. You would just do the same for a list. For a, like, 2 or 3 item list, it should be fine. There'll only be performance problems when it's bigger. But in my experience, I rarely ever set a huge data structure as an index.

when i was little one time i was trying to figure out things that you couldnt do backwards, and i distinctly remember coming up with 'what if i jumped a roof' as a response to anyone who told me to 'do it backwards' to reverse something. lol. i wonder if that be a good hashing technique?

in all reality though im having a hard time with

so

[
    c1,
    c2,
    c3,
    [
        b1,
        b2
        b3,
        [
            a1,
            a2,
            a3,
            ...
        ]
    ]
]

adding b4 would be directly visible to c but not a neccessarily a, correct?

but should that back reference be just like the rest of the properties (like __proto__ in js)? im not sure if it needs to be treated differently. does it need to be [own properties] and [inherited stuff] or can i just do [own properties, [inherited stuff, [more inherited stuff]]]

I don't really know what you mean by this. But I'm pretty sure bh's going for having [own properties] and [inherited stuff], and when you attempt to set/get a property, if it doesn't exist in [own properties] then it'll do the operation on [inherited stuff] instead. It would be a recursive operation.

The way we do messsage passing is that you call the object with a message and it reports the corresponding method, which you can then call. The procedure in the object that translates messages to methods is called the dispatch procedure. So, the dispatch procedure looks for the message in all the ones this object knows about by itself, and if that fails (no local method for this message) it just calls its parent! The parent is an object, so calling it really means calling its dispatch procedure. If we don't find the method in an object that doesn't have a parent, then we throw an error. Once some ancestor's dispatch procedure actually returns a method, then that method is called on the original object, just as it would have been for a local method. It's really neat -- the entire implementation of inheritance is

(... checks for known messages ...)
ELSE
  IF (PARENT ≠ (LIST))    (This is how a nonexistent parent is represented)
     REPORT (CALL PARENT INPUT LIST: (MESSAGE))   <--- just this line!
  ELSE
     ERROR (JOIN [NO] (MESSAGE) [MESSAGE IN OBJECT] (SELF))

A tree is a structure that includes a value and zero or more children. The value can be anything; the children must be trees. (So this is a recursively defined structure.)

A binary tree is a tree with a value, an optional left child, and an optional right child. The children must be binary trees. A binary tree with only a left child is different from a binary tree with the same value and the same subtree as its right child (and no left child). That is, it matters on which side you put the child if there's just one.

A binary search tree is a binary tree in which the values are numbers (or some other fully ordered type), and every value in the left child is smaller than the tree's value, and every value in the right child is larger than the tree's value.

So, if you're lucky, in every subtree the left child and the right child are of equal size, i.e., the tree's value is the median of all the values in that tree's children.

So how do you search for a value? Let's say I'm looking for the value x. If the value of the tree is x, I've found it. If x is less than the value of the tree, then it can't be in the right child, so I can ignore all those values (half of the values in the tree!). Recursively look again in the left child. Eventually either I find x or I hit an empty left or right child; in the latter case x isn't in the tree.

How long does this search take? At each step I eliminate half the values, so in at most $$(\log_2 n)$$ steps I reach the bottom, where $$n$$ is the total number of values in the tree.

Unfortunately, a binary search tree doesn't have to be balanced. If you build the tree in the easiest possible way, new values are always added at the bottom. So if you add values to the tree in order, then the smallest value will be in the top (root) node, the next-smallest value will be the root of the right child, etc. No node will have a left child. In this case it takes up to $$n$$ steps to find x or to find out that x isn't in the tree. This extremely unbalanced tree is essentially just a list of values.

A balanced binary search tree is one in which you use a more complicated algorithm to insert a new value. One such algorithm inserts the new value in the easy way, so it's somewhere at the bottom, then checks whether the parent of the new value is balanced or not. If not, it juggles the parent, the new value, and the other child of the parent to make that set of values balanced. Then it looks at the parent's parent, sees if it's balanced, and if not, juggle the parent's value, left child, and right child to make it balanced. And so on. working your way up the tree until the whole thing is balanced. Although the steps of this algorithm are a little complicated, each step only takes constant time, and the maximum number of steps to reach the top is $$\log_2 n$$, so search is maximally fast (because the tree is always balanced) and insertion is just a constant factor slower than the easy insertion algorithm, which already takes $$\log_2 n$$ steps.

There are other balanced insertion algorithms that use more complicated, but a little faster, algorithms. For example, look up red-black tree in Wikipedia.

so i had a project called object library with help! for 5.4.5
its such an old project and its classes don't obey bh's rule because the manual hates classes and teaches me to use prototypes so i have to figure that class out

so should the parent be a property of the object

untitled script pic(86)

or be defined separately

untitled script pic(85)

also, why is the object block returning a function, rather than the list itself?

in another project i was able to get the child / parent relationship by just having the objects be represented with associative lists, ie:

https://snap.berkeley.edu/snap/snap.html#present:Username=cameron8299&ProjectName=anob&editMode&noRun

I don't think it matters much. The first way, other objects can ask for this one's parent.

The function includes the environment in which it was created, i.e., it includes the script variables of the New Object procedure. It's only by returning an internal procedure that those variables are preserved.

Of course you can make objects as lists; in the manual I do that for prototyping oop. But what that means is essentially reinventing environments, instead of using the ones the language gives you. I do it this way because SICP does it this way, and they do it this way to make the point that you don't have to have special object-oriented languages to do oop; all you need is procedures and lexical scope. Some Lispians denigrate the importance of oop by saying "Objects are just closures."

ive played around with that but im not too keen with the whole sprite = objects idea. to me its an awkward interface ( especially untitled script pic ). not to say i dont use clones (just love mapping tiles to a grid for some reason), but if im playing with some sort of mathematical concept or working more with data structures i think it makes alot more sense to have a table look up that way i get to see everything overhead and i dont have to worry about keeping track of clones.

i almost strictly use sprites as a pen to draw representations of 'sprites' / other graphical images anymore, like

untitled script pic(1)

heres the project link to see it working

the clone just draws the shape and deletes itself. the actual positioning / interaction is handled elsewhere in a more purely mathematical sense. so instead of putting untitled script pic(2) ill say

untitled script pic(4)

with untitled script pic(5)

as my predicate(condition? test? check?, idk pick your favorite)

this way i can really understand whats going on behind the curtains and have acute control over whats doing what when and where you know

i think i have some sort of personal grudge toward built in functions lol, in snap i find myself all the time trying to reinvent already implemented blocks with the 'more primitive' blocks. i find this progression:

untitled script pic(6) = > untitled script pic(7)

untitled script pic(8) => untitled script pic(9)

untitled script pic(10) => untitled script pic(11)

oh so very much more satisfying than throwing in that black box version you guys got propped up in the palette (no offense ;)).

ok small tangent

im not a fan of the new 'features', like the variadic input blocks jens just put in im very dissapointed with because they were just lazily thrown in at the end of the ever growing labelParts object,with their own specs %sum %product , whats next %ifelseifelse? %switchcasedefault? %runerrandsanddontforgettoletthedogout...

point is sum and product are both %mult%n functionally. every %mult%_ thrown in to things like %words or %numbers is a sin. not to mention every dropdown menu, which take up a good half of the total specs, %s. for the multi slots there needs to be some sort of accountability for nesting. wasnt going to tag @jens but, an alternative that i think would help cross the gap from coding inputs in js to snap would be to have something like "%op %mult( + %opn)" where a multi arg isnt just multiple (some input) its multiple (some blockspec). and you know what would be the perfect way to represent this? the same way you already do! those little jagged blocks, should be customizable;
Screen Shot 2022-05-09 at 2.08.28 AM

but please, add functionality, not features.


so qustion for you bh, in regards to

do you agree with this sentiment? ive been peeking in on the lisp people and have to say am very intrigued by their whole philosophy and practice. i just havent gotten to jumping down that rabbit hole yet.

its a command block inside of an input slot.

you dont say

because i can turn it into something like this
Screen Shot 2022-05-09 at 6.08.48 AM
with a small bit of javascript.

if im drawing anything other than rectangles, ill use something else. it was just giving you an example, which was probably a

on my part. (had to google that one, always forget what it means)

seriously though i wasnt attacking your style, just because we do things differently doesnt mean there has to be this big issue so what exactly is your problem? if you dont like me, then by all means, leave! i mean we can sit here and play this game but you and i both know its going nowhere.

Prior to 5.0, our rule was that we wouldn't make something a primitive if it could be written in Snap!. So the higher order functions, FOR, etc. were library blocks.

But the thing is, it was super slow that way. It was okay for little toy lists (apple, orange, banana) but when Jens got interested in media computation we had to be able to operate on all the pixels of a costume, O(10K) of them. So, now we have primitive HOFs. We still hate that we had to do it, though.

On the list for someday is "hybrid primitives." They'll have "edit" in their context menu, and when you click it you get a Block Editor with (e.g., for MAP)


This function has the "(2)" in its name so that you can experiment with it while still having the fast primitive available.

This still isn't perfect because the code is given; you don't have to invent it. But you can expand on it, e.g., by having it take several list inputs and having the function take that many inputs, so you apply it to all the item 1s of all the lists, etc.

I don't care how kludgy the Snap! implementation is, as long as what the user sees is beautiful. So I'm already thinking about how to extend that feature to user defined procedures, which drives Jens crazy. But you're absolutely right, our original goal was to have the smallest possible footprint while enabling users to do everything, and it turned out to be adding just eight blocks to Scratch. See Bringing "No Ceiling" to Scratch: Can One Language Serve Kids and Computer Scientists? in which Jens and I explain all this in 2010.

I'm definitely a Lispian. The thing about objects and closures was kind of a defensive reaction to the huge fuss people started making about OOP in the '90s, thinking it's the only good way to program.

But my religion isn't Lisp; it's Structure and Interpretation of Computer Programs. You need to read it; it's life-changing.

there has to be a way to directly link snap blocks to js commands, i mean theres already that code mapping thing (which i dont understand...), but if the blocks were purely graphical representations of the underlying language, wouldnt it be possible for them to 'write' their own functions as the user puts them together? i mean is there a reason why this


javascript
// Color instance creation:

function Color(r, g, b, a) {
    // all values are optional, just (r, g, b) is fine
    this.r = r || 0;
    this.g = g || 0;
    this.b = b || 0;
    this.a = a || ((a === 0) ? 0 : 1);
}

// Color string representation: e.g. 'rgba(255,165,0,1)'

Color.prototype.toString = function () {
    return 'rgba(' +
        Math.round(this.r) + ',' +
        Math.round(this.g) + ',' +
        Math.round(this.b) + ',' +
        this.a + ')';
};

Color.prototype.toRGBstring = function () {
    return 'rgb(' +
        Math.round(this.r) + ',' +
        Math.round(this.g) + ',' +
        Math.round(this.b) + ')';
};

Color.fromString = function (aString) {
    // I parse rgb/rgba strings into a Color object
    var components = aString.split(/[\(),]/).slice(1,5);
    return new Color(components[0], components[1], components[2], components[3]);
};

// Color copying:

Color.prototype.copy = function () {
    return new Color(
        this.r,
        this.g,
        this.b,
        this.a
    );
};

snap


transition cant be made? (as in, the snap version represents the js version exactly, so you could build functions with snap blocks that run as fast as the language)(and, you see where im going with this...) snap modification via snap, like that interpreter interpreter business, compiler compiler, you know? obviously this is a bit different target than snap is aiming at but i cant help but wonder about the possibilities

also

this i agree with, i think, you mean having it to where the user can say

{
    '%numUE': {
        type: 'input',
        tags: 'numeric unevaluated'
    }
}

in a snap-interface kind of way? ive definitely attempted this before, looking for a way to have the unevaluated option on other input types, which in turn led me to write a script that took every spec, made a copy, appended 'UE', and added ' unevaluated' to all of their tags. lol, was an interesting experiment and worked perfect after working around a couple oddballs.

that sounds alot better than just adding more and more predefined input types.

and i actually found that link awhile back on one of your comments when i was still just lurking the forums, i think i skimmed through the first few chapters on break one day at work and found it quite intriguing, it probably did a little to help push me towards that whole scheme/lisp direction. still need to figure out how to get it working on my computer, so i can actually follow along.

almost missed this, very clever