Classes

Something that makes Snap! as a language feel incomplete to me is the lack of classes and the ability to extend said classes with other classes.
Take the following JS code, for example:

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}
class Rectangle {
    constructor(aPoint, anotherPoint) {
        this.bounds = {origin: aPoint, corner: anotherPoint}
    }
    width() {
        return this.bounds.corner.x - this.bounds.origin.x
    }
    height() {
        return this.bounds.corner.y - this.bounds.origin.y
    }
}
const rect = new Rectangle(new Point(0, 0), new Point(80, 50))

How would you recreate this in Snap? In theory, you could just do something like
(Point x: (# x) y: (# y)::control)::control define+ hat script variables (data) set [data V] to (([x] {report (x :: variables)} @delInput@addInput::list) ([y] {report (y :: variables)} @delInput@addInput::list)@delInput@addInput::list) report (data)
But this is both tedious to make and to use, requiring the use of this block:


(script pic was not working) to actually call reporters in the (data) variable.

I’m not entirely sure how you would implement a class editor (Though, I have a potential idea), but it would be really nice to have, and would unlock so many new possibilities within Snap!

Im currently trying to implement this in my own copy of Snap! (that I randomly add stuff to for fun, not on GitHub or anything), and i used the idea of a hat block for each class, adding constructor, functions, or other such things in its blocks, like this:

And then like a create instance block, etc etc. In fact, now that I think about it - I may just be able to implement this in raw Snap! (using the this scripts block?) I’m going to start working on that now.

Well. Several answers to this.

  1. The main answer is that we think it’s perfectly possible to have full OOP with inheritance without classes; an instance can have methods and so it can serve as a quasi-class for its clones. Prototyping OOP is unusual in programming languages, but we think it’s clearly the right thing for a language for learners, in which people tinker rather than design everything carefully in advance. We have three ways to do OOP:
  2. The natural vehicle for OOP in Snap! is the sprite. You make instances by cloning the sprite, and (unlike in Scratch) “clone” really means to share methods and potentially even variables, although variables aren’t shared by default to avoid breaking programs that give clones serial numbers by incrementing a variable in the parent before each cloning. Do you think you need a class? Just make a sprite and HIDE it! It can still have children that share its code and are shown.
  3. The next way to make objects is to use lambda to make closures, as in SICP Chapter 3:


    untitled script pic (13)
    untitled script pic (12)
    An object is represented as a function that takes a message (a text string) as input and reports a method (hence the nested lambdas). The ASK function takes an object and a message, sends the message to the object, and calls the method it gets back with any additional inputs it needs. That’s more complexity than the Point example needs, because none of its methods take extra inputs. But this way the mechanism I’m showing you is more general than just this simple case. The constructor procedure is a little complicated, but of course you can use metaprogramming to generate constructors from less detailed syntax.

Oh right, how do you do inheritance? This way:


untitled script pic (16)
untitled script pic (17)
untitled script pic (18)
If the object understands the message, it reports a method as always (such as the message Z). If not, it calls an instance of its parent class to get the method to apply.

  1. As of I think v10, you can use Snap! dictionaries (lists of name-value pairs) as objects with inheritance:


untitled script pic (13)
untitled script pic (20)

untitled script pic (16)
untitled script pic (23)
untitled script pic (25)
The “…” inside NEW 3D POINT is literally three dots, not a metasyntactic variable that stands for something else to put there.
So you see, we have plenty of ways to do it!

have a look at the OOP library, it has everything you need to make classes. I’m in a rush to the airport right now, but please check out the new Neural Networks library so see how I’m making two classes for individual layers and whole neural networks. It’s protoype based, a class is nothing but a prototype, that’s literally all there is to it.

[edit] ah, Brian beat me to it, and his answer is - as always - correct and eloquent, and he even uses SICP-stype dispatch procedures for them (in No. 3). I love this way, but a downside to closures is that you can’t edit them later on in their life cycle to add more dispatch cases or even just to iron out glitches and bugs. That’s why we’ve added list- / dictionary- based data objects, which you can also build yourself, but for which we’re supplying a library.

With dictionary based objects you simply do inheritance by cloning a prototype, or by setting an object’s “parent” attribute to another one.

Have fun wherever you’re going!

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