Giter Club home page Giter Club logo

Comments (25)

tmickel avatar tmickel commented on July 26, 2024 9

After #280 I think we have a feature complete implementation of Scratch 2.0 custom blocks. Will be looking at the custom reporters issue sometime in the future.

from scratch-vm.

tmickel avatar tmickel commented on July 26, 2024 8

@brianharvey It would be great to talk to him as soon as we're ready to implement! I know there are many great ideas in the Snap code. Thanks for mentioning that.

from scratch-vm.

kyleplo avatar kyleplo commented on July 26, 2024 8

Would this be anything like what you are thinking?
scratchblocks

from scratch-vm.

tmickel avatar tmickel commented on July 26, 2024 7

Sorry - I definitely don't mean for this to be a certain confirmation. We have a lot of work to do to determine if it will be included in Scratch 3.0.

But, we want to get started playing with the idea in scratch-vm to make sure we at least have the right code structure in its early stages to support things for this (I did mean "custom reporters").

I appreciate the excitement though :)

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024 2

About the C-shaped hat block: Are you saying that the RETURN REPORT block would be the bottom of the C? That's clever, I like it. But I see two minor disadvantages to consider. (1) I fairly frequently forget to click the reporter shape in the make-a-block dialog and then fix it in the Block Editor by clicking the hat block. That lets me make the new block a reporter shape in the prototype in the hat block, but it doesn't automatically add a REPORT block. You might want to do that, provided that the user hasn't already put a REPORT block at the end by hand. (2) Maybe nobody but me will ever do this, but once in a while I write a reporter in which the one REPORT block isn't at the lexical end of the definition but is inside an IF inside a loop. The way we start reporter definitions, with a detachable REPORT block under the hat, makes it easier to think of that solution than if the REPORT were glued to the bottom of the definition.

PS Hi, @rdococ, @towerofnix, haven't seen you folks on the Snap! forum in ages.

from scratch-vm.

Kenny2github avatar Kenny2github commented on July 26, 2024 1

I'm surprised I haven't commented here before, but here's some opinions I have.

I think this should be done with minimal change to what Scratchers are already used to. What we currently do is designate a variable to return the result:
see link
So what I think custom reporter logic could look like is something nearly identical:
see link
An early return would still be accomplished by stop [this script v]:
see link
As the comment notes, having a set reported value to () permanently attached to the define hat would dodge the question of null values by explicitly initializing the return value. (Wording bikesheddable of course - "this block's value"? "result"?) It would also make it obvious when a function is pure - a pure function would only use the pre-attached block.

I don't think it's Scratch's job to teach kids about functional programming – Snap! does that just fine! – and this seems to me like a minimally invasive bridge between Scratch's extremely imperative nature and other programming languages' ways of returning values, including Snap!. I think a move towards exactly what Snap! does, while likely to be welcomed by fans of higher ceilings and wider walls (such as myself!), would IMO effectively require other supporting infrastructure (like a scoped variable construct) to be successful, of which the necessity of introduction would be a barrier to the existence of custom reporters in the first place.

My opinions are not as strong as my wording may make them seem; this is just my two cents.

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

Title is slightly confusing.. @tjvr noted this: https://scratch.mit.edu/discuss/post/2044070/

You realise that could be parsed as β€œImplement ((custom blocks) and (reporters))”, right? :P

from scratch-vm.

tmickel avatar tmickel commented on July 26, 2024

A couple of notes about implementation of procedures in 2.0:

Behavior is to yield if a call is detected to be recursive:
https://github.com/LLK/scratch-flash/blob/9c0728bfbe2f2ed1cff1c9f3af8d2a55552b99a5/src/interpreter/Interpreter.as#L662

Detecting if a call is recursive seems to do a 5-layer thread stack search: https://github.com/LLK/scratch-flash/blob/5cff62b909856b7d7b3d116a5dcc2b4f03de8482/src/interpreter/Thread.as#L98

To me this looks like it can cause weird behavior, e.g.: https://scratch.mit.edu/projects/119356687/

Seems preferable to do an N-layer stack search (with whatever fast implementation) unless there was some reason for the 5-limit.

Procedures affect the "stop this script" block, which calls this: https://github.com/LLK/scratch-flash/blob/9c0728bfbe2f2ed1cff1c9f3af8d2a55552b99a5/src/interpreter/Interpreter.as#L672

The other interesting feature is the "warp thread", which:

  • is turned on by setting the procedure as "run without screen refresh"
  • prevents all enclosed blocks from yielding
  • has an extended timer (warpMSecs)

Params are dealt with separately from general Scratch variables (primVarGet vs. primGetParam). This of course makes sense - the values for the params come from activeThread.args which is basically kept in a stack.

from scratch-vm.

mrjacobbloom avatar mrjacobbloom commented on July 26, 2024

In Scratch 2, arguments aren't separate for each thread, afaik there's one copy of each argument in memory no matter how many levels of recursion are happening. Would it be possible to fix this in Scratch 3?

Edit: it actually looks like this issue was resolved. Never mind!

from scratch-vm.

TheBrokenRail avatar TheBrokenRail commented on July 26, 2024

I made a version of custom reporters that works using stackFrame but it just freezes the block see my branch 'patch-15'

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

Are you guys talking with Jens about this? We've already been through a lot of the implementation issues about custom reporters and he probably would have useful insights for you.

from scratch-vm.

greenSnot avatar greenSnot commented on July 26, 2024

scratchfoundation/scratch-blocks#904 This is the easiest way to add custom block(procedure) I think.

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

If they're called "reporters," the return block should be called "report"! :) But yeah, that's great! Now on to first class lists...

from scratch-vm.

jessiejs avatar jessiejs commented on July 26, 2024

Let's get some more activity for this issue, it's a much-needed feature.

I want to propose an alternative design for reporters, we have the return block, but also the hat block is a C shape, with a return statement attached to it, so you have to make it return something.

I think this will make this more understandable to newcomers, while not compromising the high-ceiling principle, as you can also return in other places in the function, there just has to be one at the end.

from scratch-vm.

rdococ avatar rdococ commented on July 26, 2024

This feature has the potential to close almost every reporter suggestion on the forums simultaneously, make all sorts of other suggestions (first-class lists, script variables) much less cumbersome to work around, and better introduce kids to real programming concepts in an intuitive way. Heavy bump.

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

Hey, bh! πŸ‘‹ 🌊

I fairly frequently forget to click the reporter shape in the make-a-block dialog and then fix it in the Block Editor by clicking the hat block. That lets me make the new block a reporter shape in the prototype in the hat block, but it doesn't automatically add a REPORT block. You might want to do that, provided that the user hasn't already put a REPORT block at the end by hand.

This is a good note, but - not sure it would be applicable for Scratch. It doesn't seem likely to support switching a procedure to a reporter, or vice versa - frustrating though it is to forget to choose the right shape in the first place. Can see that being an okay option if the block hasn't yet been attached to any scripts at all, but otherwise you have to have "bumping" logic to slot-out nested custom reporters, or un-stack stacked stack blocks, when switched to the other shape. Not sure if that's easy to explain to kids - nor conditionally disabling the feature in a scenario where it'd cause bumping.

If it's just not a bit of functionality in the first place then there shouldn't be an issue.

It's possible the "make a block" button would pop up a preliminary dialog that asks you - neigh, forces you - to choose which shape you want the block to be, before entering details like labels and inputs (and whether or not the block is atomic). It's still possible to choose the wrong shape, for sure, but seems a bit less likely - and forces deciding the shape to never be an afterthought, since it's so fundamental to how the block works and how you will use it and code it.

Maybe nobody but me will ever do this, but once in a while I write a reporter in which the one REPORT block isn't at the lexical end of the definition but is inside an IF inside a loop. The way we start reporter definitions, with a detachable REPORT block under the hat, makes it easier to think of that solution than if the REPORT were glued to the bottom of the definition.

Personally - would fully admit this is super confusing ✨

What's the return value (hem, report value!?) for a reporter that didn't run a report block? The empty string? Null? Infinity, or beyond?

It's not that this is unreasonable behavior - functions which report something some of the time and a generic, but quite literal "nothing to report here" value at other times can be useful. But it's a rare use case, and it's hard to convince myself that of an instance where an arbitrary, language-determined "fallback return value" is more appropriate than one that I explicitly wrote myself. (Sort of programmer who checks if (result === null), rather than if (!result), and all that. But then, we also think null and undefined are readily effective as having different meanings... but only if you make that meaning yourself... because JavaScript's inclusion of both is very half-heartedly "nothing," thanks to the language's history.)

Forcing a report block at the bottom (by baking it into the hat, which, yes! I like it too!) is a way of ensuring that Scratchers at least think about it, and quite likely decide what's going to go there. Because you can't get rid of it, even if you decide the "report" block belongs inside an "if" - you will stll have a "report" block stuck at the end, so it's still reminding you, hey, look at me! you might want to give me a value anyway!!

If you really don't want to give it a value, then yeah, it's bothersome. Personally we often use control structures like switch/case where the structure as a whole will certainly return, even if it doesn't have a default (because we know all the possible values for the expression being matched...) - and so it wouldn't make sense to have an additional return beneath the end of the control structure.

That said, Scratch doesn't have quite as much opportunity for this since the only real "do something or other" block is if/else. For that block in particular, well, it will at least give Scratchers an opportunity to realize that the stuff that comes after an "if condition, return" will never be evaluated if that condition is met, and so "if/else" is unnecessary and the hat-baked report block is suitable for the "fallback" return value... but, you know, it's still a little bit of a kludge.

Here's another hole-poking angle: What if Scratchers think the built-in report block, because it's so specially always-present, will always run, no matter what other code is in the block? And is that really such a wrong perspective, for someone who doesn't know better, to come upon?

Scratch already has a block for stopping a custom block early - it's "stop this script". (We were very excited about this in 2015.) The "report" block would just be... the same thing... except with more functionality! Functionality that might not be very obvious at first glance. So now "stop this script" is dubiously useless in a reporter, and the meaning of "report" is a little muddied, AND you have to ask yourself, oh no, what will it do when we DO use "stop this script", since we canceled before getting to the always-present "report" block!?

Well, we're either stuck in the same boat as optional report (returning some fallback value like an empty string or zero or false), or... we admit the kid who thinks "report" block is special might be right... and run that always-present "report" block. So "stop this script" wouldn't mean "abort abort abort", it would mean "skip to the report block (which was going to get run anyway, right?)".

Anyway, that's all the manifesto-in-principle for just not having a "report" block that you can move around at all. Huzzah! Now the code is too simple to get confused by. Party, party.

That would mean you can't change what's going to get reported in the middle of a block - that is, not directly. You would have to use a variable, or some other state-mutating code that gets reflected in the block's "report" - and then "stop this script" to skip ahead and cancel any later code. (That's normal code, not special code, not 100% baked into your custom block code.)

"Well, now, how exactly is that better than just using a variable to return from a normal custom procedure in the first place!? What about my recurrrrsions?" (The answer to the second question is tail-call recursion, of course :gobo_smile:)

I don't necessarily think this is the best possible idea - or even necessarily good - but I do think it has an assembly-like simplicity to it, and it turns out that Scratch being about as simple as assembly code has always been a strength, not a weakness. (By this point, Scratch definitely would've become Snap!-but-less-cool if it were a weakness, you know.)

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

switching a procedure to a reporter

You mean "switching a command to a reporter." They're all procedures! :)

In Snap!, when you click on the hat block of a custom block definition in the Block Editor, if the block is in use in a script somewhere, you see this:
change-without
but if the block isn't in use you see this:
change-with
I guess a user might get confused about what determines whether or not you can change the shape, and maybe we should have a message at the bottom of the first flavor saying "You can change the block shape only if it's not in use anywhere" or something.

I suppose at the root of the difference is that we give Command as a default. I often think that there shouldn't be any defaults anywhere, but that's another discussion! But yeah, if you don't have a default, the mistake of forgetting to make your custom block a reporter is a lot less likely, I agree.

What's the return value (hem, report value!?) for a reporter that didn't run a report block? The empty string? Null? Infinity, or beyond?

It should be an error. "X didn't output to Y" is the Logo error message, where Y is the procedure to which this invocation of X is supposed to provide an input. But I just checked, and we report an empty string, which is how we represent null. Following you guys, Jens really hates error messages and tries to make anything you can construct have a meaning. I, on the other hand, would much rather get an error message right where I made an error, rather than get a wrong ultimate answer that's hard to debug because the actual error is nested three deep in procedure calls. Or get a wrong answer and not notice because I'm doing some hairy numerical computation that I can't do in my head. But arguably the best design principle for you is different from the best for us because your audience is younger.

But an even more general design principle is that we catch errors at runtime (when we do at all), not while you're constructing a script. The reason is that we can't always catch an error without running the script; this case we're talking about now is kinda the halting problem, so we know we can't catch it in general.

I'm not arguing for a reporter only sometimes reporting. The case of interest is reporting early, as in
untitled script pic (2)

Forcing a report block at the bottom (by baking it into the hat, which, yes! I like it too!) is a way of ensuring that Scratchers at least think about it, and quite likely decide what's going to go there. Because you can't get rid of it, even if you decide the "report" block belongs inside an "if" - you will stll have a "report" block stuck at the end, so it's still reminding you, hey, look at me! you might want to give me a value anyway!!

Hmm. Yes, forcing the user to think about it is probably good, but sticking a REPORT under the hat block probably does that too. And putting it in the hat block has the potential to make a user think you can't report anywhere else.

the meaning of "report" is a little muddied

It's a cap block, no tab under it, so that should help somewhat.

I think the whole argument about "stop this block" is a red herring. Why would anyone put that in a reporter?

So "stop this script" wouldn't mean "abort abort abort", it would mean "skip to the report block (which was going to get run anyway, right?)".

But that's not what "stop" means in normal English! I think that'd be confusing.

Also, I've just noticed that you're saying "stop this script" rather than "stop this block." We have both of those, with different meanings, and that's super important, especially if you care about recursion, but even if you say to stop inside a loop inside a definition. "Stop this script" means to stop the toplevel script, in Snap!, and that's worked very well for us.

You would have to use a variable, or some other state-mutating code that gets reflected in the block's "report" - and then "stop this script" to skip ahead and cancel any later code. (That's normal code, not special code, not 100% baked into your custom block code.)

Ugh bletch. If you're going to make people use variables in that way, you might as well not have reporters! The whole point is for kids to learn how powerful composition of functions is, and how powerful functional programming is altogether. Imho. And you sorta recognize that in your next paragraph:

"Well, now, how exactly is that better than just using a variable to return from a normal custom procedure in the first place!? What about my recurrrrsions?" (The answer to the second question is tail-call recursion, of course :gobo_smile:)

I note in passing that not all recursion is tail recursion. Computing an element of Pascal's triangle is a great paradigmatic recursive function.

I do think it has an assembly-like simplicity to it, and it turns out that Scratch being about as simple as assembly code has always been a strength, not a weakness.

Assembly code isn't simple! It's a pain in the butt and back when we all programmed that way, before you whippersnappers were born, we had zillions of bugs. I agree with being simple, but you should aspire to being as simple as lambda calculus, which really is simple. :~)

(By this point, Scratch definitely would've become Snap!-but-less-cool if it were a weakness, you know.)

As you know, that was our goal from the beginning, for Scratch to take in our ideas. I hear the argument about not confusing the tiny tots, but y'all said that about lists, at first; even after you had an implementation you were afraid to give it to users because people wouldn't understand it. But what finally happened is that, yes, a lot of kids didn't understand it at first, but that didn't chase them away; they just cheerfully used the parts of the language that they did understand. (This is the great advantage of not being in a school, but never mind...) And many of them did eventually take on lists, starting by copy-pasting other people's code, and eventually did come to understand lists, which they wouldn't have, if you had forever withheld them. I think the same thing applies to any powerful idea. If you put, say, first class functions in the language, some kids, maybe most kids, wouldn't understand them at first, but they'd keep using Scratch and eventually at least some of them would figure it out.

When I was working at Atari, I remember one of their bigshot game designers, I forget who, pointing out that if they'd somehow introduced a modern video game to kids back when all we had was Pong and Space Invaders, the kids would have had no idea what to do and would have been scared away -- but today, kids don't have to start with Pong and Space Invaders and gradually work their way up to modern games; those games are in the culture, and kids see their older siblings play them, and they jump in and fail for a while, same as learning to skateboard. Kids are way braver than I am.

I expect I'll visit Boston some time in the spring and maybe we can get together. I've never seen your new digs!

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

Thank you, Brian. Appreciate that this sort conversation sort of managed to represent a microcosm of the tale-as-old-as-BYOB, "This will help teach kids code!" "No!! No!! Sir, they've never seen a list before, think of the children!!" discourse.

Some quick terminology notes:

Also, I've just noticed that you're saying "stop this script" rather than "stop this block." We have both of those, with different meanings, and that's super important, especially if you care about recursion, but even if you say to stop inside a loop inside a definition. "Stop this script" means to stop the toplevel script, in Snap!, and that's worked very well for us.

Right - thought about mentioning that since we know you've got both and it works wonders for you!

"Stop this block" feels particularly crucial in a world where you can have custom C-blocks, which is something I've wanted for Scratch forever, but one thing at a time, alas...

There's some strange history with "stop this script" - it is maybe an accident (or emergent behavior from implementation) that it worked for stopping custom blocks in the first place. It didn't work for stopping "run without screen refresh" custom blocks - it would just cause the Scratch interpreter to hang up. That bug being fixed is what we were celebrating in the link project - I think it helped cement the behavior of "stop this script" (in custom blocks), but really that behavior was never exactly right to begin with...

I don't know if "stop this block" is the best term either, though. I wonder if "this block" sounds like... well, literally just the "stop this block" block. The cap shape on the end helps signify that it's affecting the rest of the context it's in, of course... but I think if we saw similar behavior in Scratch, I'd want to add two options: "stop this custom block" and "stop this loop". Maybe even "stop this iteration", too, or something similar. I'm biased because I come from JavaScript and C-like languages and all that, but I feel it makes sense to separate return (or "stop this thread" so to speak) from break. And that continue is one honkin' good idea and it's a shame Scratch doesn't have it yet!

You mean "switching a command to a reporter." They're all procedures! :)

This could just be a misinterpretation on our part, but since Scratch 3.0 became a thing we've always thought of "custom procedure" as meaning "custom command block", mostly due to internal terminology - which we might've misread! Anyway, yes, agreed. For sure, there's no way the word identifying a stack or command block in the dialog would be "procedure".

But that's not what "stop" means in normal English! I think that'd be confusing.

I 100% agree. I don't think it's a good idea for "stop" to mean "stop, except psst, skip ahead to this part by the way shh". I'll go into this more in a minute!

It should be an error. "X didn't output to Y" is the Logo error message, where Y is the procedure to which this invocation of X is supposed to provide an input.

I love errors. I'm sorry! I know Jens doesn't and I know Scratch doesn't, but I'm totally with you there. It's only slightly an exaggeration to say we've spent the last year of our life trying to make better errors in our own code. Seriously!

But Scratch just isn't going to get errors. It's out of the picture. (Yes, even if the halting problem weren't real, it would probably still be against Scratch ethos for the editor to automatically detect and warn "watch out, your reporter might not report".)


I think the most charitable way to have the "stop" block alongside a "report" block that's always stuck at the bottom of the code would be like this:

  • Add "stop this custom block" and automatically replace "stop this script" in old projects with "stop this custom block", when they're positioned under a custom block, since that's what those usages meant and did.
  • When you drag out a "stop this script" block and change it to "stop this custom block", inside a custom reporter, automatically make the block morph into "stop (this custom block) and report (VALUE)", with value being an empty text field (or boolean, as appropriate).
  • Like mentioned, keep the "report" block that's stuck at the bottom of the reporter's definition.
  • Don't add a new "report" block in the block palette. Instead, make it so trying to drag out the stuck-in-place "report" block will create and pick up a "stop (this custom block) and report" block, instead.

I think the whole argument about "stop this block" is a red herring. Why would anyone put that in a reporter?

Right! It doesn't mean anything. But it IS the same block that ALREADY exists to do other kinds of "stopping", and reporting early means stopping.

I don't think it makes sense 1) to have two blocks that mean "stop", especially if (only!) one of them literally has "stop" in the name; 2) to have one of the options of one of those blocks, namely "stop this custom block", be "wrong" to use in one specific context (custom reporters); and 3) to have that second stopping block, "report", ONLY mean ANYTHING in that same specific context, meaning nothing in every other context.

That's why I think "stop and report" or "stop this custom block and report" would work better. It combines the two blocks into one, makes sure the word "stop" is always there when you're doing something that involves stopping, and gives meaning to a block that otherwise would always be contextually inappropriate ("stop this custom block", no report).

It also forces you to always think about what you're going to report. Keep in mind "stop" means "stop something", not necessarily "stop this" - the REAL signifier, like you mentioned with report, for "stop this", is the block being capped off. There is another option, "stop other scripts in sprite", where the block morphs into a non-capped stack block. That's all to say that "stop this script" would obviously stay capped, because nothing more is going to run, but the morphing when you make it into "stop this custom block" is itself a signifier that the stuff that's OUTSIDE this custom block is going to continue to run, and since you are a reporter, it will matter what value you report!

And trying to drag out the "report" block from the end, and getting "stop this custom block and report" instead, indicates that if you want to use it early then you really are stopping early. It's a different-looking block because it has different meaning, "stop here early and report", not "OK, we're all done, now report". It incorporates the "stop" term that's already commonplace and means what it says on the tin - and also incorporates the "report" term, too, because that's also relevant, and something you always have to think about when you're working inside a reporter block.

Ugh bletch. If you're going to make people use variables in that way, you might as well not have reporters! The whole point is for kids to learn how powerful composition of functions is, and how powerful functional programming is altogether. Imho.

I agree with being simple, but you should aspire to being as simple as lambda calculus, which really is simple. :~)

I think the same thing applies to any powerful idea. If you put, say, first class functions in the language, some kids, maybe most kids, wouldn't understand them at first, but they'd keep using Scratch and eventually at least some of them would figure it out.

Thank you so much for being extremely clear and vocal about this, seriously!!

It's exactly what I want the Scratch Team to hear, LOL. You know how it's been, you all have been trying to convince them for many years longer than us (and we've been trying for years too, cough...).

To be honest, I think the biggest problem with "stop, but PS, also skip to the end" is that, even apart from being utter nonsense in a vacuum, it's really not how any other programming languages work. What exactly is that teaching? It's an artificial problem with an artificial solution.

That's what I really want to point out. I want custom reporters to be as easy to learn as they can be, but also just as powerful and beautiful as they are in all of computer science and functional programming and OOP and beyond.

I hope this comment has made that much clearer. I'm poking at current ideas so that we pay more attention to them and are more critical to their benefit, and also exploring alternatives, however bad they are, to help get the intuition rolling.

I expect I'll visit Boston some time in the spring and maybe we can get together. I've never seen your new digs!

πŸ‘€ We'll have to email about it! We've been meaning to visit some friends in America sometime quite soon and Boston would 100% be on the way there. It'd be a miracle if the timing for anyone and everyone works out, but, you know, you aren't going to catch a miracle if you don't reach ✨ πŸ‘Ύ

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

I appreciate that that syntax involves using "stop this script" for early returns. It's not a killer issue IMO, but having less overlap between two blocks' behavior generally makes the ideas easier to learn.

I'm concerned about the capabilities for (non-iterative) recursion, but I guess not fatally so. In order to maintain an upper state that persists across recursive subcalls, we currently have to use a stack list. Any approach without lexically scoped variables by definition doesn't change that. (Custom blocks' inputs are close, but not quite, since they generally can't be overwritten - it would be the same with your "set return value to" block, since you haven't included a way to read the current return value.)

But that's sort of what you're arguing: Well, we do that in Scratch already; so is continuing with that approach really much more of a problem?

In their nature, reporters are always and only syntax sugar - they eliminate the need for using a separate variable/stack list item/register/etc to track an immediately used result. That's all reporter syntax does in any programming language!

If we don't want to fundamentally expand the data processing capabilities of Scratch, then I'd say your suggested approach to reporter syntax is quite effective. And that's a good thing - we don't need to alter the language of Scratch to encourage users to think about creating their own blocks which return things.

The main consequence of not implementing a new data storage system (lexical scoping), while introducing something that would employ that system of it were present, is that people will be faced much more often with coming up with and utilizing workarounds (like using a stack list, but there are other creative solutions too).

That's IMO something of a strength to Scratch. To be honest, "validating workarounds" is maybe one of the better things a new feature can do β€” because "workarounds" are really just employing creativity to come up with a solution in an environment that doesn't give you easier to use tools from the get-go. They're most of how Scratch teaches more advanced programming. And if you aren't put into situations where you need to use a workaround, well... you probably won't, and you'll be left not learning what you could have.

There are obvious major advantages to not keeping people working with workarounds (gestures: recursive behavior is technically possible without custom blocks at all, but how many people really explored that in Scratch 1.4...? ditto with clone-like behavior: most people just duplicated their sprites!). But in this case it might be better off - for now - to go with a solution that doesn't hinge on a fundamental new system (say it with me... lexical scoping!).

(Edit: I acknowledge that lexical scoping is itself a kind of "syntax sugar" on top of, you know, the underlying implementation, and so are probably all programming language features! I'm clearly biased and just feel stronger about the behavioral implications of lexical scoping compared to those of immediate-access reporter values. Mayhaps because I don't see any advantage to continuing on without custom reporters - I don't think forcing people to stick their values in a one-time variable polluting the sprite's variable list is teaching them much, and the educational workaround - stack lists, etc - would still be applied with more advanced use of custom reporters!)

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

Well, first, a technical quibble. You keep saying "lexical scope," but that's not a requirement. Dynamically scoped Logo does recursive reporters just fine. What you need lexical scope for is rolling your own object system, or referentially transparent higher order functions. And yes, arguably that's the point at which you should just send kids to Snap!.

Maybe more than just a technical quibble. Let me throw out dynamic scope as a serious proposal. I know all the reasons why lexical scope is considered preferable for adult programmers, but (as I think the discussion above is meant to imply) it's a tricky idea for kids. Dynamic scope has the great simplifying virtue of not separating scope from extent. If a variable exists (and isn't shadowed), you can use it. That's why Logo is still dynamically scoped after all these years. It's not especially inefficient if you use shallow binding in the implementation.

I guess one question is, when users ask for custom reporters, what is it they're asking for? What new expressive power will this feature give them? I'm not sure I know the answer, but I think (non-tail) recursion is part of it.

Back in the day, I happened to be visiting the Media Lab just in time for a Scratch Team meeting on the subject of how to design custom command blocks. The then-current proposal, which someone had actually implemented already, was that they wouldn't have inputs, because the whole mechanism of formal parameters and their bindings was thought to be too complicated. And then I said, "How does this differ from BROADCAST AND WAIT?" And they found that convincing, which is why Scratch custom blocks do allow inputs. (And that turned out not to be too complicated for kids!) I think this is a similar decision point. Custom reporters that add no expressive power over custom commands would be a lost opportunity. Imho.

Arguments about syntactic sugar leave me cold. Every syntactic feature of every programming language is "just" syntactic sugar over lambda calculus. (Or, if you prefer, over Turing machines.) Here's what truly recursive reporters buys you:
untitled script pic (2)

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

Thanks for the lovely counterargument :) I do dislike the general tendency to shuttle away genuinely valuable suggestions because "you can do that already with XYZ workaround, it's just worse". Like in my edit, all programming language features are just "syntactic sugar" - but what we should really be saying is "abstractions" - and abstractions in the fabric of a programming language aren't a bad thing. It's what makes them languages!

Here's what truly recursive reporters buys you:

I would love to understand the differentiation point between a "truly recursive reporter" and one that, well, isn't "truly" recursive. That is expressive power - like you say, recursion is IMO one of the biggest points of custom reporters in the first place. But does "truly recursive" mean something different from "just recursive"?

I don't think anyone here would advocate for custom reporters that don't support recursion. But custom statement blocks do support recursion, already. It just doesn't look very nice:

Screenshot 2023-12-12 at 4 42 18β€―PM

As far as I can tell, the basic implementation of custom reporters - no extra frills, simply a custom block that looks like a reporter and any inline equivalent to "report" - would allow the exact definition you shared (less the multi-branch if-else), because we already support recursion and so would custom reporters.

But I could be mistaken. I feel like I'm missing what you mean by "truly recursive", or you aren't aware that existing custom blocks support recursion (which would surprise me), or there's an insidiously fundamental bug in my definition above that I'm not picking up on.

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

I think your version works, but it took me a while to convince myself about this part:
Screen Shot 2023-12-12 at 1 09 17 PM

What I meant by "truly recursive" is "not tail recursive," i.e., "branched recursive." That's what gives you increased expressive power compared to a loop. In my version, the two recursive calls that provide inputs to OR aren't tail calls, and, as you point out, you needed an ugly workaround there.

I guess I should have picked this example:
untitled script pic (3)

untitled script pic (4)

(By the way, if you drag that example picture onto your desktop you can then drag it into a Snap! instance and play with the code. My favorite recent feature!)

I'm pretty sure you can't do that with commands without an explicit stack. (If I were really doing this in Snap! I'd use a list as the COINS input rather than a sentence, but for the purpose of comparison with Scratch this way is fairer.)

Oh, let me speak to the argument that making people use explicit stacks is good for their souls, like hair shirts. :) For one thing, without first class lists it's needlessly hard to build a stack of arbitrary data structures! I guess you can fake it with something like csv. But, that aside, it seems like a rather unScratchly argument. It would be a good argument if your goal is to teach university-level CS 2, but the whole philosophy of Scratch, the reason they abandoned "no ceiling" for "wide walls," is to enable kids to express creativity without having to study computer science. Arguing for requiring explicit stacks seems to me like arguing that Scratch should allow only one toplevel script so that kids will learn to write their own scheduler.

But I guess I shouldn't try to teach the Scratch Team about Scratch philosophy. :/

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

And then I said, "How does this differ from BROADCAST AND WAIT?" And they found that convincing, which is why Scratch custom blocks do allow inputs. (And that turned out not to be too complicated for kids!)

I am sometimes concerned with how much fundamental features of the Scratch programming language or editor turn out the way they do because someone on a whim came in and said, "Hello, let's at least consider doing this differently...?" (I would not have stuck with Scratch nearly this long if they didn't land my pull requests to sort the variable menu and the custom block palette alphabetically, rather than almost 100% arbitrarily. Those PRs were thrown together in a day by someone who definitely is not part of the Scratch Team!)

But I guess I shouldn't try to teach the Scratch Team about Scratch philosophy. :/

That's rather nonsense! Clearly if we don't try, it won't change. Otherwise, how would we have custom block inputs?

That said, the part that really gets me about it is that you and I (and surely others as well) just happened to show up at critical moments and threw our opinion into the pot right when it'd get noticed. If you came to the Scratch Team in 2015 saying "ah, I see you've been enjoying custom blocks without inputs for three whole years, would you like... to reconsider...?" then it probably would have gone nowhere. And maybe if I'd made the PRs sorting custom blocks last week, they'd have been merged, but would that be in 2023, or would it be in 2026...?

I don't want to be harsh on the Scratch Team's time availability for developing the language (or editor) after it's been released - obviously there are layers to it. But yes, it almost always feels like fighting a (peacefully conversational) battle that isn't even going seen, let alone engaged, let^2 alone challenging, well, philosophical attitudes toward the language or its development.

(By the way, if you drag that example picture onto your desktop you can then drag it into a Snap! instance and play with the code. My favorite recent feature!)

Now this is stunning :) I love it.

For an extremely short period, there was a feature on the Scratch forums where you could convert posts with blocks embedded BBCode [scratchblocks]-style into entries in your real Scratch 2.0 backpack. It got removed very quickly (sadly), but it was quite magical. This is sort of like that, but five times cooler!

What I meant by "truly recursive" is "not tail recursive," i.e., "branched recursive." That's what gives you increased expressive power compared to a loop.

I thought it had to be a differentiation between recursion and iteration, yes - but I've only just begun to dig into the difference in the wizard book and still don't feel all the way comfortable telling them properly apart, to be honest! (Yes, I see the classic coin counting example.)

This one isn't too bad, but I suspect I may be cheating:

Screenshot 2023-12-12 at 6 48 54β€―PM

Because we're accumulating a value, we don't need to return to a previous value. So there's no need to track old values separately, and no need for a stack.

But I'm really using dynamic scope here, mutating a variable (that is conveniently reset at the start) as I go. I don't think I can do this without that scoping "trick".

Here's a different way which uses a helper to store a value (note that we never reset the variable, and use "set" blocks at each return, instead of a lone "change by 1"!):

Screenshot 2023-12-12 at 7 05 01β€―PM

"Say" block included for demonstration purposes:

scratch-coins.mp4

Yes, Scratch's scoping for custom block input values is doing all the work here. We do get our own stack frame per level of recursion, we just have to work to use it.

I don't know if using inputs this way counts as an "explicit" stack. It certainly is a stack, but it's hooking into Scratch's provided one rather than making our own (with a global list). You can (sort of) run these simultaneously and they won't clash with each other. With a "wait 0.1" at the very top of the main "no-dynamics:" definition, and a "wait 0.05" to ensure they don't steal values from the "return" variable blocks between each other:

Screenshot 2023-12-12 at 7 14 33β€―PM

(This still takes about a minute to complete all its steps. To be honest, I was worried it wouldn't work, LOL.)

For one thing, without first class lists it's needlessly hard to build a stack of arbitrary data structures! I guess you can fake it with something like csv.

Yes! I don't like it. For dictionaries or for things which include references to other things (e.g. nested lists), I usually keep a live store made out of multiple lists each containing a common field of whatever dictionary, and just pass around references (i.e. indexes) between custom blocks. (Those references have to get garbage collected periodically, and it's a huge bother.)

Arguing for requiring explicit stacks seems to me like arguing that Scratch should allow only one toplevel script so that kids will learn to write their own scheduler.

And I absolutely agree with this (argument! not forcing every project to be 1s1s! ✨). I think that's a great way to put it.

If the underlying, common concept isn't so bad to teach to kids - and really, it is probably almost always not so bad... - then I see very little harm in giving people better tools to express themselves with.

Learning how to write a scheduler, or how to use a stack, or how to manage a flippin' garbage collector - those aren't low floor obstacles. If you know how to do them, they do raise the ceiling rather high, but, you know, with constrained walls - you have to figure those underlying systems out for yourself, or else you just can't make use of them. It's a real barrier for a lot of people and a lot of ways of coding. Like you said, most ten year olds aren't here for university-level CS 2.

from scratch-vm.

towerofnix avatar towerofnix commented on July 26, 2024

Quick addendum: I made my own scripts (the change-making ones, not the "sentence matches pattern" one, sorry!) into an interactive Scratch project: https://scratch.mit.edu/projects/939250428/

from scratch-vm.

brianharvey avatar brianharvey commented on July 26, 2024

Here's my project (ironically command-based):

https://snap.berkeley.edu/snap/snap.html#present:Username=bh&ProjectName=count%20change

from scratch-vm.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.