'report' block halting execution in the middle of script

im working on implementing javascripts prototype system in snap and im coming across what i think is a bug. i have this script to test out getters/setters:

js2 script pic

the js2 script pic (1) block automatically checks for properties with either a value or a getter, and if it finds a getter, it calls it with the object bound to js2 script pic (2). which it does correctly:

js2 script pic (3)

but when i run that first script, in the editor it just appears as if its reporting nothing (no bubble), but the results pic shows:

js2 script pic (4)

so for some reason calling the getter ends up reporting in the higher context. why.

project link

Probably for the same reason that
Untitled script pic (3)
reports 1? (I mean, it did even when FOR was a library block written in Snap!.)

But, why do you want to use that horrible Javascript pretend-prototype OOP system? What they've implemented is a class/instance system, but for some political reason I don't understand they felt they had to call the class a "prototype."

Instead, read Section VIII.D, page 88, of the manual to see how real prototyping OOP works!

re OOP:

Hmm... you know, I've always felt like OOP is the one thing I'm very confident about, but lately folks keep peddling strange remarks that make me question whether I know anything at all. JavaScript's object system, the pure and plain one, not the recent module additions, are - as far as I can tell (see my previous disclaimer) - exactly the same as Snap's sprites, i.e. Henry Lieberman style dynamic delegation prototypes. The problem is, that every programmer only ever knows Java and Python, and therefore has been pestered to reproduce the "classical" definition of "inheritance, polymorphism, encapsulation" in their school tests. And that class based definition (which also obsesses about distinguishing classes and instances) is something they sorely miss in a real prototype system like pure JavaScript, and therefore everybody as their first act of treason to Henry ends up inventing their own class system ontop of JavaScript's prototypes. In doing so they, of course, all demonstrate the intellectual superiority of prototypes :smiley:

but im not reporting anything in my loop! the only other report block is in the getter, which is called inside the o.val block, so what it should do is simply pass the value into the add to collection block, not report from the main script. thats like if i said

js2 script pic (8)

and the report block inside of foo cause the whole script to stop and also report foo.

because i dont (or didnt) know how it worked. it confused the hell out of me. but really going through it and modeling an engine from scratch (or, snap), im able to further appreciate the intricacies of the language. its so cool to me. and javascript literally is a prototype system, everything is an object, it doesnt have classes, every object has an internal [[Prototype]], which points to another object (or null). this other object is the prototype. if you try to access a property or method on an object and it doesn't exist on that object, it will look for it on the prototype. how is that not prototype inheritance? and @jens did you just come here to argue with bh? can you tell me whats going on with my script?

yes, I did indeed just come here to point out to Brian that JS is, in fact, a real Lieberman style prototype system, not just a politically green-washed class hierarchy in disguise :slight_smile:

The mechanics of report are geared towards doing what the average Snap! user will expect, with a tendency towards exiting from the top-most script instead of the inner one, but with subtle tweaks to enable seamless semantics in user-defined control structures. In general report should work pretty much exactly how you'd expect it to work inside your own custom blocks. Your system looks like it's trying to define methods as rings, which is fine, but here the preference to exit from the top script rather than from an inner one will bite you. As a solution try avoiding report inside control structures in your rings, and a more Smalltalk-style block closures approach. Or, much better yet, check out the manual section Brian mentioned to you, it's literally gold!

Lieberman: Every object can have its own methods and data. It can also have a parent, which is a separate object, from which it inherits stuff.

Javascript: Every object has to have this other object, its prototype, to hold its methods. The object itself just has non-method data. The prototype can inherit from a parent.

That thing they call a "prototype" is just a class in sheep's clothing.

Outside the context of computer science, what "prototyping" means is that you make an actual thing, the prototype, and then you make other things like it. The prototype is a thing, not a description of how such a thing should behave. (In that kind of real-world prototyping, you don't have inheritance; the second thing you make has all its behaviors built in.)

In computer science, the point of prototyping OOP is to support tinkering, as opposed to design. You don't make a Dog class, with a Dog.prototype that holds its methods. You make Fido, who's a cocker spaniel who likes to eat Cheerios and who's learned to play dead and fetch sticks but who thinks that sitting up and begging for food is beneath his dignity. Fido is entirely self-contained. Then you make Spot, who's very similar to Fido except that he prefers caviar over Cheerios and who's okay with sitting up and begging but who farts a lot. Fido is the prototype for all the other dogs you make later.

Yeah, that makes sense. If you don't understand something, build it yourself.

But now that you understand it, it's so complicated! I could barely follow your code.

I didn't bring up the example of FOR because it's a loop. I brought it up because it's a C-shaped block, and when you use it inside another custom block, a REPORT in the C-slot reports from the caller, not from FOR. It took us a long time to work out all the details of how REPORT should behave so that, as Jens said, it does what users want most of the time.

You could probably use CATCH and THROW to accomplish what you want.

@jens @bh so this works:

js2 script pic (10)

weird

edit, now its just working

js2 script pic (11)

i dont know what i did but ok

Awesome! Don't question it. :~)

Hmm... man, I dunno. I'm damn sure that I can stick methods into any JS object, in fact, I do it all the time, even in the Snap source. JS objects are just a namespace and you can stick anything into any slot, either data or a function, which is is also just data, duh. It's a single namespace for everything, no distinction between methods and other properties, I would expect you to totally love its simplicity. Yes, every object also has a built-in prototype slot, but you don't have to use it, and for sure there's no special gimmick for where methods get stored. I may be wrong but imho JS is exactly Henry-style delegation.

If you don't have to use it, then yes, I guess they support simple prototyping, but they also support, and apparently recommend, this other thing. All your objects in Snap! use their prototypes to hold their methods! It's a totally unnecessary complication.

And I claim the reason they invented that kludge is that they were brought up in class/instance OOP and couldn't wrap their minds around the wonderful simplicity of prototyping.

@bh @jens

so i was/have been working on this geometry construction app, and i make all my objects follow a class/instance structure,

snippet
class Curve {
  static all = [];

  constructor() {
    this.types = ['curve'];
    this.hidden = false;
    this.color = '#F80';
    this.children = [];
    Curve.all.push(this);
  }

  delete() {
    this.children.forEach(child => child.delete());
    var all = Curve.all;
    all.splice(all.indexOf(this),1);
  }

  hide() {this.hidden = true;}
  show() {this.hidden = false;}
  lock() {this.locked = true;}
  unlock() {this.locked = false;}

  distToPoint(point) {
    return dist(this.closestPoint(point),point);
  }

  closestPoint(point) {
    return this.parametric(this.closestT(point));
  }

  drawObject(ctx, drawfn, args) {
    var alpha = ctx.globalAlpha;
    if (mouse.lastSelected === this) {
      ctx.lineWidth = 3;
      ctx.globalAlpha = alpha/2;
      drawfn(...args);
    }
  
    if (mouse.hovering === this) {
      ctx.lineWidth = 2;
      ctx.globalAlpha = alpha;
      drawfn(...args);
    }
  
    ctx.lineWidth = 1;
    ctx.globalAlpha = alpha;
    drawfn(...args);
  }
}

class Line extends Curve {
  constructor(p1, p2) {
    super();
    this.types.push('line');
    this.p1 = p1;
    this.p2 = p2;
    for (let arg of arguments) arg?.children?.push(this);
  }

  move(dx, dy) {
    this.p1.moveBy(dx,dy);
    this.p2.moveBy(dx,dy);
  }

  draw(ctx) {
    var dx = this.p2.x - this.p1.x;
    var dy = this.p2.y - this.p1.y;
    var len = sqrt(dx * dx + dy * dy);
    var ux = dx / len;
    var uy = dy / len;
    var x1 = this.p1.x - ux * 10000;
    var y1 = this.p1.y - uy * 10000;
    var x2 = this.p2.x + ux * 10000;
    var y2 = this.p2.y + uy * 10000;

    this.drawObject(ctx, drawLine, [ctx, x1, y1, x2, y2]);
  }

  closestT(point) {
    const { x: x0, y: y0 } = point;
    const { x: x1, y: y1 } = this.p1;
    const { x: x2, y: y2 } = this.p2;

    const dx = x2 - x1;
    const dy = y2 - y1;
    return ((x0 - x1) * dx + (y0 - y1) * dy) / (dx * dx + dy * dy);
  }

  parametric(t) {
    return this.p1.add(this.p2.sub(this.p1).mul(t));
  }
}

class Segment extends Line {
  constructor(p1, p2) {
    super(p1, p2);
    this.types.push('segment');
  }

  closestT(point) {
    return max(0,min(1,super.closestT(point)));
  }

  draw(ctx) {
    var x1 = this.p1.x;
    var y1 = this.p1.y;
    var x2 = this.p2.x;
    var y2 = this.p2.y;

    this.drawObject(ctx, drawLine, [ctx, x1, y1, x2, y2]);
  }
}

class Ray extends Line {
  constructor(p1, p2) {
    super(p1, p2);
    this.types.push('ray');
  }

  closestT(point) {
    return max(0,super.closestT(point));
  }

  draw(ctx) {
    var dx = this.p2.x - this.p1.x;
    var dy = this.p2.y - this.p1.y;
    var len = sqrt(dx * dx + dy * dy);
    var ux = dx / len;
    var uy = dy / len;
    var x1 = this.p1.x;
    var y1 = this.p1.y;
    var x2 = this.p2.x + ux * 10000;
    var y2 = this.p2.y + uy * 10000;

    this.drawObject(ctx, drawLine, [ctx, x1, y1, x2, y2]);
  }
}

is there a better way to do this? like, am i over complicating things by doing object orientated style? i guess i dont really understand how prototypes make things simpler than class/instance systems.

Yes, I'm following a class-like constructor pattern for all properties (not just methods!) initially, because JS engines are optimized for such patterns. But later on in the lifecycle of individual objects ("instances") there are numerous occurrences where an "inherited" method or datum is temporarily or permanently replaced by another one. One example: When checking whether a bunch of mutable things are identical inside Snap! I'm adding a temporary datum to one of them, and then check all the others for the existence of that datum. But that's just one example, making an "instance" behave individually different from it's "class" is a pattern you'll notice throughout Snap's code. Point is: JS is, indeed, a true prototypal system.

@bh @jens how dare you two hijack my thread and then ignore me! rude! im eager to learn and i thought you guys were teachers. and im interested in what youre talking (arguing) about so what gives?

Yeah, sorry, I was about to answer your question when I fell asleep. I've just gotten up for today and here I am answering you before I even read my email! :~)

Well, first of all, a terminological quibble: Prototyping is OOP; it's just a different approach to OOP from class/instance.

Second of all, this is just me talking; Jens probably wouldn't say what I'm about to say, and the rest of the world definitely wouldn't.

So. When you're designing a software project before you build it, there's nothing wrong with class/instance OOP, and I guess there's nothing wrong with what I still say is the peculiar Javascript approach of building a prototyping OOP system and then uglifying it by turning the concept of a prototype into a thing, the Prototype, that's provided as a sort of sidekick of every object, turning prototyping into a religion.

Where prototyping really shines is not in designing a project, but in diving in and building a system interactively, without designing. That is, the kind of exploration or creativity you see among Scratchers.

Snap! kind of has a foot in each world. We are definitely a flavor of Scratch, even though we're no longer a mod of Scratch, as BYOB was. We want to foster creativity and spontaneity. At the same time, the reason we felt the need to build something beyond Scratch is that (as you say) we're teachers, and we want to teach Computer Science to teenagers, and it follows that we also want to support the kind of project design you're doing.

So, no, I don't think you're overcomplicating your project by using classes and instances.

Side note: I'm a bad computer scientist, because I don't have the patience to design things. I just jump in and start coding. My friend and colleague Mike Clancy has characterized my programming methodology as "debugging a blank piece of paper." If I were building your project I'd probably start by making a sprite called P with a small filled circle as its costume, and teach it to respond to click and drag, and then I'd make the next point by making a child of P called Q. And then I'd make a line sprite called L that would have two points as sprite-local variables, and set them to P and Q. And so on. But your way is better, for big complicated projects.

Class/instance is terrible for tinkering. You have to work and work before the first thing happens on the stage. That was my point about the dogs. If you're telling a story about your dog and your friend's dog, you don't want to start by designing a Dog abstraction. You want to take a picture of Fido, preferably in front of a green screen, and load it into Snap!, and then start teaching him to do tricks, by programming methods. And then you want to add your friend's dog Spot, who's like your dog only different. There's no Platonic ideal Dog in your program, just dogs.

haha, to hijack this thread even further I'd like to contradict you, Brian, in your prediction that I'll disagree with you on this. On the contrary, you've nailed it!

Just noticed this. Yes, it's easier to implement class/instance than prototyping. And I guess that's why JS built a class/instance system, called the classes "prototypes," and then stuck real prototypes (i.e., parents) in as a special case.

Sorta like building a language around named procedures, then adding gray rings as a special case, instead of having procedures be anonymous by default and then adding a naming mechanism. :~P

you know, I've been racking my mind where your misconception about JS prototypes comes from, and I remember that we used to read all kinds of prototype-inheritance papers when we designed Snap's current system, and one paper that particularly confused us was the one about SELF, because it had a kind of pseudo-class in addition to prototype parents, and we decided that we'd rather to Lieberman instead.

Yeah I think whatshisname the JS guy must have read that paper! :~)