scratchfoundation / scratch-gui

Graphical User Interface for creating and running Scratch 3.0 projects.
https://scratchfoundation.github.io/scratch-gui/develop/
BSD 3-Clause "New" or "Revised" License
4.46k stars 3.55k forks source link

Global custom procedures #4858

Open towerofnix opened 5 years ago

towerofnix commented 5 years ago

Ever since the dawn of the custom procedure, Scratchers all around the world have risen up to ask: How can we define such blocks to be usable across all sprites?

For reference, in Scratch, custom blocks are presently specific to the sprite they are defined in. So, you select a sprite, then click "make a block"; this makes a proc definition block ("define (my block)"; it looks similar to a hat block) show up in the workspace. This is then usable anywhere within that one sprite, but not in any other sprites (unless you copy the definition over manually).

Of course, there's no question that global custom procedures would be useful. Just as an example, one could create a single definition for a general utility block, useful in any sprite. (This will be especially frequent when custom reporters and C-blocks are implemented.)

The question that's long been at hand is simply "how?".

I'd like to draw an interesting parallel between custom blocks and extensions. When you load an extension into the project, a new palette containing the blocks of that extension is added. Sometimes, two blocks - each from different extensions - share the same name, or a name that is very similar. In order to make these blocks distinguishable, each extension has its own icon, and that icon appears on every block:

Three blocks, each from a different extension.

I think something like this could be tied into global custom blocks. Essentially, I figure a custom block's definition will still belong to a specific sprite, but it will be accessible from the block palettes of all sprites. And beside every use of that custom block will be an identification of the sprite - probably a text label of the sprite's name, or maybe an image of the sprite's costume. (The latter raises the question of "what if that sprite's costume changes", and doesn't work well if two sprites have very similar costumes, or an empty costume.)

To actually make a custom block global, there would be a checkbox option when creating/editing it - "for all sprites" or "for this sprite only". (If you're editing a block and it's already used in other sprites, the option to disable it (and make it non-global) would be disabled.)

While a custom block would act upon the sprite calling it (e.g. changing the caller's position, not the position of the sprite in which the block is defined), it'd be able to call non-global custom procedures defined within the same sprite as it is; so for example, you could have custom blocks like the following:

Three custom blocks: "calculate costume dimensions", "hone in on edge distance", "hone in on touching-edge size"

The first custom block is the one other sprites will make use of. The other two are helper custom blocks, which means the first custom block makes use of them, but they don't need to be exposed to other sprites. So, only the first would be global.

Tl;dr: Place a sprite's name (or image) as a label/icon beside global custom blocks, similar to extensions. Make a checkbox for "for this sprite only" (default) or "for all sprites" in the custom block dialog. Global custom blocks operate on their caller, but can make use of helper custom blocks defined in the same sprite.

Thoughts and input? I'd love to see this implemented and I'm sure countless other Scratchers would, too.

towerofnix commented 5 years ago

cc @thisandagain? This's been open for a couple weeks; I'm obviously not expecting any conclusions on this right away but just labeling/acknowledging it would be appreciated!

sfederici commented 3 years ago

Will this ever be taken into consideration by the Scratch development team? I really cannot find really useful usages for local procedures. Instead they are vital when you have multiple sprites sharing a similar behaviour. I really do not understand the effort behind creating local-only procedures

benjiwheeler commented 3 years ago

One thought: a design principle of Scratch is that it have a "low floor", so kids who are programming for the first time don't face too many new concepts or questions between intention and result.

Custom procedures are already an advanced concept for new Scratchers, and even some teachers aren't familiar with them.

So I would guess that adding a local/global question might increase the friction too much.

Now, what if we just treated all custom procedures as global? That wouldn't surprise new Scratchers. There is the issue of ambiguity if two have the same name, but that could just be forbidden (for creating new ones), and if they already have the same name, we could just put the sprite name in parenthesis next to it.

sfederici commented 3 years ago

what if we just treated all custom procedures as global? That wouldn't surprise new Scratchers. There is the issue of ambiguity if two have the same name, but that could just be forbidden (for creating new ones), and if they already have the same name, we could just put the sprite name in parenthesis next to it.

I agree. Global custom procedures are certainly better than just local costume procedures. (NOTE: Scratch users should already be accustomed to local/global variables)

If the problem is just knowing were the procedure is defined, this could be easily overcome by adding an "edit procedure definition" option in the right-click menu (already available via search box in Griffpatch's Scratch Addon)

towerofnix commented 3 years ago

(NOTE: Scratch users should already be accustomed to local/global variables)

This is an important point! Still, I agree that global custom procedures are more valuable than only having local ones. If Scratchers want to say that a custom block should only be used in certain situations, they can always annotate the name of the block, like with sprite labels as mentioned earlier. (Also, custom blocks are sorted alphabetically, so if a user is worried about having too many blocks in their palette, they can use prefix strings to help organize.)

griffpatch commented 3 years ago

I think you'd be getting into a can of worms there with local variables or sprite costumes, or sounds... anything else that is local to one sprite... Plus having them all as global would cause havoc with larger projects that already have loads of custom blocks ;) I would imagine if anything, global custom blocks would have to be defined only on the stage where there are only global variables and no costumes, etc? I love the concept, but I'm not super keen on the reality of the implications.

On Tue, 23 Feb 2021 at 14:52, (quasar) nebula notifications@github.com wrote:

(NOTE: Scratch users should already be accustomed to local/global variables)

This is an important point! Still, I agree that global custom procedures are more valuable than only having local ones. If Scratchers want to say that a custom block should only be used in certain situations, they can always annotate the name of the block, like with sprite labels as mentioned earlier. (Also, custom blocks are sorted alphabetically, so if a user is worried about having too many blocks in their palette, they can use prefix strings to help organize.)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/LLK/scratch-gui/issues/4858#issuecomment-784256785, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTM3PWMTFRYJML3K2PFB2DTAO6JNANCNFSM4HP6BR5Q .

sfederici commented 3 years ago

Creating global custom blocks in the Stage is not something that I'm against to. So, if this is the solution, it is fine to me. What is really important is having useful procedures in Scratch. Local custom blocks, in my view, are really specific to "one sprite" projects, to make the code more organized. But in a concurrent language like Scratch, procedures are really meaningful when you are allowed to share code among similar (not identical, i.e. not clone) sprites.

Global procedures in a block language have been successfully implemented since 2010 in Jens Moenig's BYOB and now in its more advanced Javascript implementation called Snap (https://snap.berkeley.edu/). Jen's code is really neat and clear. It shouldn't be too difficult to reimplement it in Scratch.

towerofnix commented 3 years ago

But in a concurrent language like Scratch, procedures are really meaningful when you are allowed to share code among similar (not identical, i.e. not clone) sprites.

As a side-note, this has prompted me for years (and probably many other developers) to use a sort-of "clone role" system, where distinct, concurrent objects with shared code (utility functions, some common behavior, etc) are implemented as a single sprite, with each "type" of sprite having its own "role" variable. This acts as a string identifier for the role of that clone; hat blocks like "when I start as a clone" and "when I receive" always check this role variable against the role that script "belongs" to. This all enables pretty easy sharing of code across semi-distinct objects, and it has other benefits as well; however, it can lead to some really unwieldy sprites, with upwards of thousands of blocks, many of which could better be divided across other sprites. Global custom blocks would be a huge help here!

sfederici commented 3 years ago

to use a sort-of "clone role" system, where distinct, concurrent objects with shared code (utility functions, some common behavior, etc) are implemented as a single sprite, with each "type" of sprite having its own "role" variable. This acts as a string identifier for the role of that clone; hat blocks like "when I start as a clone" and "when I receive" always check this role variable against the role that script "belongs" to. This all enables pretty easy sharing of code across semi-distinct objects, and it has other benefits as well; however, it can lead to some really unwieldy sprites, with upwards of thousands of blocks, many of which could better be divided across other sprites

It sounds interesting but I can't imagine a usage right now. Can you give me an example?

towerofnix commented 3 years ago

It sounds interesting but I can't imagine a usage right now. Can you give me an example?

Here's a project I made a few years ago: https://scratch.mit.edu/projects/114570911/

In the main "obj" sprite, you can see off the bat a script ("when I receive game.init") which sets up the clones with the appropriate "self.tag" variable; then if you scroll to the right, you can see lots of scripts (like "when I receive") which check that variable. Most of them use shared custom blocks, like my own version of "go to x y" which sets data to make the position of that clone available to all other sprites, and some other utility functions. (You can scroll down from the "init" script to find those.)

Those custom blocks are important to be shared across every object defined in that sprite, but it would be handy if I could just declare the custom blocks once and reuse them across multiple sprites! After all the objects are pretty much distinct, besides the shared procedures: they all have their own behavior and costumes, using the tag variable to keep each clone acting only its own job.

griffpatch commented 3 years ago

I guess one way of allowing this would be to allow the creation of 'sprite templates', where you create a new 'template'. It looks similar to another sprite, having its own variables & custom blocks (and possibly sprites and sounds... unsure about that bit). Then you can opt to implement a template for any other sprite such that it inherits the variables and scripts from the template. For this to work well though you'd want to be able to create abstract custom blocks (lol) in the template which then would appear as define blocks in the inheriting sprites :)

But really - this is way too complex for the scratch philosophy :D

On Thu, 25 Feb 2021 at 12:33, (quasar) nebula notifications@github.com wrote:

But in a concurrent language like Scratch, procedures are really meaningful when you are allowed to share code among similar (not identical, i.e. not clone) sprites.

As a side-note, this has prompted me for years (and probably many other developers) to use a sort-of "clone role" system, where distinct, concurrent objects with shared code (utility functions, some common behavior, etc) are implemented as a single sprite, with each "type" of sprite having its own "role" variable. This acts as a string identifier for the role of that clone; hat blocks like "when I start as a clone" and "when I receive" always check this role variable against the role that script "belongs" to. This all enables pretty easy sharing of code across a script, and it has other benefits as well, but it can lead to some really unwieldy sprites, with upwards of thousands of blocks, many of which could better be divided across other sprites. Global custom blocks would be a huge help here!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/LLK/scratch-gui/issues/4858#issuecomment-785862814, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABTM3PTEHXSAU44HA7M5BADTAY7SBANCNFSM4HP6BR5Q .

sfederici commented 3 years ago

But really - this is way too complex for the scratch philosophy :D

Yes, even this proposal sounds interesting. But I agree, thinking Scratch-wisely, this can be too complex

sfederici commented 3 years ago

Here's a project I made a few years ago: https://scratch.mit.edu/projects/114570911/

Yes, this is exactly the kind of projects where global custom blocks can be useful. Using clones (that is, basically, one sprite projects) can be a strategy, but it is not natural at all with all the tags and tests

ihadastrok commented 1 year ago

@SimonShiki what are you saying? I think you're saying hgudsgiudsvjdsgvucuduvdasguidsahgudascygvfregyv2f34yvt7qf4wt68g4qwf578gf4578gg7t886tvu8tgetPrimitives () { return { control_repeat: this.repeat, control_repeat_until: this.repeatUntil, control_while: this.repeatWhile, control_for_each: this.forEach, control_forever: this.forever, control_wait: this.wait, control_wait_until: this.waitUntil, control_if: this.if, control_if_else: this.ifElse, control_stop: this.stop, control_create_clone_of: this.createClone, control_delete_this_clone: this.deleteClone, control_get_counter: this.getCounter, control_incr_counter: this.incrCounter, control_clear_counter: this.clearCounter, control_all_at_once: this.allAtOnce }; } 87tgetPrimitives () { return { control_repeat: this.repeat, control_repeat_until: this.repeatUntil, control_while: this.repeatWhile, control_for_each: this.forEach, control_forever: this.forever, control_wait: this.wait, control_wait_until: this.waitUntil, control_if: this.if, control_if_else: this.ifElse, control_stop: this.stop, control_create_clone_of: this.createClone, control_delete_this_clone: this.deleteClone, control_get_counter: this.getCounter, control_incr_counter: this.incrCounter, control_clear_counter: this.clearCounter, control_all_at_once: this.allAtOnce }; }

Wowfunhappy commented 1 year ago

Now, what if we just treated all custom procedures as global? That wouldn't surprise new Scratchers. There is the issue of ambiguity if two have the same name, but that could just be forbidden (for creating new ones), and if they already have the same name, we could just put the sprite name in parenthesis next to it.

But if all procedures were global, where would the procedure definition be? To me, it feels very weird for a procedure to get used by all sprites, but for the definition to only exist in one specific sprite. Adding an icon a la extensions, as towerofnix suggested, doesn't fundamentally solve this inherent weirdness.


I really like the idea of custom blocks created on the stage being global, and custom blocks in sprites being local. The only tricky thing is that motion blocks (and some others) aren't available on the stage.

I think it would make sense for motion et al blocks to appear in the block palette on the stage only once a global custom procedure has been created. This keeps the floor low for beginners—who won't be using custom blocks—without hampering more advanced coders. These blocks could also be translucent until connected to a custom block definition.

Secret-chest commented 1 year ago

Just let custom blocks be made global and appear in any sprite, but store them in the stage. This is how it is done with variables.

Wowfunhappy commented 1 year ago

Just let custom blocks be made global and appear in any sprite, but store them in the stage. This is how it is done with variables.

Right, but to reiterate what I said above: How do you add move 10 steps to a custom block whose definition is stored on the stage, given that motion blocks are not available in the block palette when the stage is selected?

(IMO, the best solution is to make these blocks just appear in the stage's block palette once a custom block has been added.)

sfederici commented 1 year ago

To me the definitions of all (global) procedures should be visible in all sprites (it is this that you were suggesting @Secret-chest?) so that I can update the definition whatever sprite I'm working on. Then they can be stored in whatever sprite we like (I would say the first sprite; storing them in the stage could be weird for movement blocks). And the definitions of all global procedures that do not contain movement blocks should be visible (and updatable) in the Stage too

Secret-chest commented 1 year ago

What's the first sprite?

sfederici commented 1 year ago

What's the first sprite?

Just the first sprite in the list of sprites. If this sprite gets removed, they could be moved to the new first sprite (or lost if the last sprite is removed). But they could also be stored in all sprites, as, when a procedure is updated, all procedures with the same name in all sprites areas should be updated accordingly.

towerofnix commented 1 year ago

To me the definitions of all (global) procedures should be visible in all sprites so that I can update the definition whatever sprite I'm working on.

This is basically in line with my stance (today) too. I think it's important to be able to make quick adjustments to a custom block no matter where you're working. The issue is the physical space scripts occupy: the 2D code area is generally organized in a certain way for each sprite in a complicated project so that code is easy to navigate and locate. We need to figure out where scripts will be placed if they're shown in all sprites — overlapping with another sprite's code clearly isn't an option.

In my opinion, it would be effective to put global custom blocks in a dedicated coding area, off to the side in the sprite list, maybe the same column as the stage. Keeping them in one space lets users stick with the existing paradigm of physically placing scripts near or apart from each other for their own personal organization preferences. And in Scratch 3, switching to one or more other sprites and then returning to any previous will remember where the script area for that sprite specifically was scrolled/zoomed to. This significantly decreases the weight of switching between sprites / code areas, making a separate area for global custom blocks more workable. (It would be nice if switching code areas with a lot of scripts were more performant, though.)

sfederici commented 1 year ago

it would be effective to put global custom blocks in a dedicated coding area,

To me, used to Snap, seeing the definitions of procedures in the script area is weird too. I think Snap's idea (being able to see the procedure definition, in a popup window, only when you ask to modify a custom block) is the correct one. So, I agree

Wowfunhappy commented 1 year ago

To me, used to Snap, seeing the definitions of procedures in the script area is weird too. I think Snap's idea (being able to see the procedure definition, in a popup window, only when you ask to modify a custom block) is the correct one.

Snap's approach is better for more advanced users. However, I think Scratch's approach of keeping everything in the script area is enormously helpful for children.

To me the definitions of all (global) procedures should be visible in all sprites (it is this that you were suggesting @Secret-chest?) so that I can update the definition whatever sprite I'm working on. Then they can be stored in whatever sprite we like (I would say the first sprite; storing them in the stage could be weird for movement blocks). And the definitions of all global procedures that do not contain movement blocks should be visible (and updatable) in the Stage too

I think it would be extremely confusing if definitions disappeared from the stage once a movement block was added to the definition. I'd rather have the definitions just always appeared on the stage, even if they contain blocks not normally available there. (It's not like you can't already drag these blocks to the stage.)

Otherwise this seems like a fine approach to me.

sfederici commented 1 year ago

However, I think Scratch's approach of keeping everything in the script area is enormously helpful for children.

No objection.

it would be extremely confusing if definitions disappeared from the stage once a movement block was added to the definition

Good to me. But I'd prefer the movement blocks being skipped when running the procedure in the Stage in this case (in Snap is different: the procedure will raise an error)

Wowfunhappy commented 1 year ago

But I'd prefer the movement blocks being skipped when running the procedure in the Stage in this case (in Snap is different: the procedure will raise an error)

Yes! This is what already happens if you drag a motion block onto the stage from another sprite or the backpack, so I would expect procedures to behave the same way.

sfederici commented 1 year ago

Yes! This is what already happens if you drag a motion block onto the stage from another sprite or the backpack, so I would expect procedures to behave the same way.

Good