Do Scratch broadcasts really support modularity?

I was unaware that there was a rationale behind the Scratch Team's decision to implement 'broadcasts' instead of a 'typical' message-passing system, but I don't think that it supports the stated goal of modularity.

Broadcasts are always global. The sender cannot direct it towards a single recipient, and recipients don't know who the sender is - and no other data can be passed with broadcasts without the aid of global variables, or even lists to keep track of it all.

This seems to be on purpose in an attempt to encourage modularity, but I personally think that it does the exact opposite for a few reasons:

  • Like global variables, there can be conflicts between broadcast names. If you have two modules that happen to use the same message name, you'll have to change every reference to it in one of the modules.
  • If you want multiple instances of a module, such as sprite duplicates or clones, you can't figure out which ones sent the messages.
  • Broadcasts and their inflexibility encourage the usage of global variables to pass data between them, which are themselves a big no-no for modularity.
  • Scratch's broadcasts are not thread-safe. If you send a broadcast that a recipient is already running through, it will restart from the beginning. This is useful sometimes, and there is probably some reasoning for why it's like that, but it seems pretty broken.

Is there a better way for a Scratch-like programming language, aimed towards a similar age range as Scratch, to support modularity?

I've always thought the same.

And when I noticed that there was this flavour of broadcasting:

image

I became confused what the semantics of the plain broadcast block is.Will every listener start processing the message but the sender can proceed at any time?

What story are we to tell students? The broadcaster is like someone who suddenly shouts out something and (eventually or immediately?) everyone who is listening for this "message", hears it and begins to react? But if it is broadcast and wait the shouter magically knows when everyone has heard his or her message and has finished reacting? And as rodococ mentions, what is the story if in the meanwhile someone shouts the same message?

I just tried

image

and it quickly reported a stack overflow. But the STOP button seemed to have no effect. Nor did clicking on this a second time. The red glow remains.

And regarding modularity, yes, neither the sender nor the receiver need know each other which is a kind of modularity. But it says nothing about how one part of a program depends upon other parts to function correctly. Looking at the "module" that does the broadcasting one knows almost nothing. In contrast, with message passing the sender either created the intended recipient of the message or received it in a message (ignore global variables considered harmful). So there is some hope one can reason about what the sending module does without inspecting the whole program.

I find it very weird that this sprite turns but never moves:

image

this (based on a post by @jens) fixes that behavior by getting the broadcast out of the receiving thread, not that I think this is intuitively obvious behavior
launch-broadcast

(why is my script pic so large?)

It is also foreign to me that this notion of event passing enhances modularity. I'm teaching myself to think of these events as if they were low-level hw events, and message passing the way I'm familiar with it can't really be based on it -- in it's current form.

Because you have a retina display, so your software is generating pictures at twice the resolution it's claiming to use. This is a pain in the butt. Luckily, if you go to the preview side of the display as you're entering the message and hover the mouse just under the bottom of your picture, a set of magic buttons appears and you can click on 50%.

If broadcast helps modularity, does inheritance hurt it? "Modularity" is very low on my list of things to worry about in my programming, honestly. Broadcast is convenient because I don't have to worry about who's doing what with the signal. But before we had OOP, I fairly often found myself constructing messages that had the sprite name and some message inputs as part of the string, e.g., sprite3/gotoxy/100/-100. And then I had to write a parser in the when I receive any message script.

Inheritance helps modularity because it reduces copy/paste re-use errors. It's really better at enforcing common behavior. In the sense that I want separate modules to have consistent behavior when they interact, then, I suppose inheritance can help that. Messaging in the sense you described, allows asynchronous processes to communicate clearly while decoupling their behavior. For me, it's not the fire and forget broadcast that's key, because that is similar to publish/subscribe, or raw hw events, which is one part of decoupling behavior. To really coordinate a bunch of cooperating processes, though, you need the more detailed messages that include data and sender id. But, that's possible outside of the broadcast functionality, so, all is good, I think (haven't actually done this yet.)

FWIW, I'm opposed to the whole remote procedure call/invoke method on object paradigm, precisely because of the tight coupling it introduces between two processes, even when something like Dbus allows you to do this asynchronously. It's as if you sent someone a letter and a hand reached out, grabbed you by the neck and said, "Get me some bacon, now!", then wouldn't let go until you did. Meanwhile, the sender is stuck with his arm in the mailbox, waiting for his bacon.
Ok, I've outed myself. :slight_smile: Not that I haven't made good use of this functionality in SNAP!

I'm not following your script pic instructions at all. This is on a Windows 10 laptop with an Nvidia graphics card, so a fair number of pixels, but not retina. What's the 'preview side of the display'? When I right click on a block and choose 'script pic', I just get a file requestor. Hovering has no effect anywhere that I can see. This is in the V6 beta, btw.

I personally think that inheritance isn't suited for the task that it's often used for - code reuse. If you want to reuse the behavior defined by a different class or prototype, the best thing for that is composition! A car can move thanks to its wheels, but a car isn't just a heavily modified wheel.

This solves many problems that inheritance has - multiple inheritance and the diamond problem, collisions between property and method names, and so on.

I also think that code reuse is an important part (if not the whole point!) of modularity, so I'd say that inheritance does facilitate modularity to some extent... but composition does it way better, and if either one was going to be included in a Scratch-like programming language, I'd much prefer composition to inheritance.

That's a fair judgement if you're mainly concerned about creating cool Snap! projects, and creating cool projects is pretty fun, but Snap! is also an educational tool, and modularity is an important concept for software development in general.

That said, this thread isn't in 'Feature Requests' because I didn't intend this to be a request for a Snap! feature, but simply a discussion about an existing feature that originated in Scratch. It could still be inspiration for future Snap! features or educational tools, though!

For previously mentioned reasons, I think that composition would be better suited to that purpose than inheritance.

All is sort-of good for Snap!. Within Snap! itself, you can easily send information to other sprites by ignoring broadcasts entirely, and just using "tell", "ask", and "of" (if you want to run a custom block from the other sprite), but it's a bit clunky (especially when you have to use "of"!).

Scratch doesn't have that, though, so the only ways for sprites to communicate with other sprites are the over-simplified broadcasting system and global variables. Neither of them are nice to work with.

I would agree here. If you're just sending a message to someone, you don't always need to wait for a reply back - and even if you do, you can do other stuff in the meantime.

I didn't mean in Snap!, I meant in the forum:

It's not quite that bad, because our object (sprites) can multithread, so can be reading the paper while getting your bacon. But I think of it differently. Our objects aren't corporations doing arm's-length negotiation while limiting their exposure; they're friends, who value being immersed in one another's lives. (This is true even if the project is a video game where you're supposed to shoot zombies; the metaphor, inherited from Scratch, is that the sprites are actors, who are collaborating to put on the show, even if their roles are enemies in the story. That's why they appear on the "stage" and wear "costumes.")

Oh, indeed -- are you telling me programmers out there in the real world make that mistake? As we say in AI circles, inheritance is a KIND_OF relation, not a PART_OF one! What inherits from car is electric-car, or maybe tank.

What we have in Snap! that's related to PART_OF is nested sprites.

the metaphor, inherited from Scratch, is that the sprites are actors, who are collaborating to put on the show, even if their roles are enemies in the story. That's why they appear on the "stage" and wear "costumes."

As someone who over 40 years ago, being excited about the "actor" model of computation, named my first language "Director" and another one "Intermission" I now think it is a flawed metaphor. It is part of the constraining Centralized Mindset (ironically one of Mitch's best papers is Beyond the Centralized Mindset). (To be far to the Actor people, despite using the name "actors" there was no director or global script.)

Sprites should be thought of autonomous beings, not subjects of a puppet masker or theatre director.

The contrast of viewpoints is stark in NetLogo where programs are always asking sets of turtles/agents to do things rather than giving those agents autonomous behaviours.

Yes, I agree about autonomy. What I wanted to get from the actor language is just the idea that, in a play, the actors are all friends and allies, even if the characters they portray are enemies. This is why I can't take seriously the whole information-hiding idea, in the context of programming for kids.

"information hiding" sounds way too negative for what is called "encapsulation". I'm thinking of it more in the sense of "doesn't require to know the internals of something to interact with it". If you think of encapsulation this way it's really just literally your famous lecture about abstraction, Brian.

Okay, yeah, I'm for abstraction. :~) But, for example, we Schemers teach students to build a data abstraction out of constructors and selectors. Other people build it into their languages, so that the abstraction is enforced on the students.

Sometimes a nice simple abstraction won't do, for efficiency reasons. The internet stack in an operating system is the standard example. And so people who've built abstraction-enforcing into their programming language end up inventing metaobject protocols to try to have their cake and eat it too. I (as you can tell by looking at me) prefer to eat my cake, and what this means in terms of languages is that I want the language to let me use just as much abstraction as I choose, including when it comes to communication between objects.

But I'll give you this: In the color library there are four blocks meant for users and 21 helper blocks. It'd be nice to have a way to hide the latter (although some are taken from other libraries, and if the user loads that library then there's no reason for us to have a second, hidden copy). But if we did that, there would be lots of implications, starting with the one that we'd have to get around to doing the transitive closure in export blocks. This is really part of my broader idea that libraries, once loaded, should be just like primitives in every way we can manage, e.g., if an error is signalled inside library code, it is attributed to the user script that called a library block, not to some internal block where it really happened.

But within code the user wrote, we shouldn't enforce good behavior, except maybe washing one's hands before using a public keyboard.

What happened to the idea of using closures to build objects in Scheme? Closures impose strict information hiding, don't they?

I guess so. But when I built an OOP system (as a big macro) it automatically added getters for all the instance variables. :~)