An idea for custom reporters
An addon could allow creation of custom reporters somehow by making a hidden sprite-scoped variable SA-REPORTER-<custom block ID> and disguise the block to set it as "return", putting it in the custom block palette. Then, the custom block would be shaped as a reporter if it has a return, and when it is used in a script, it would actually be saved as an use of the special variable preceded by the custom block.
I know this is probably hard to implement but I am leaving the idea, maybe it can be made more easily.
Confusing could you provide more details on how this would work.
Have SA create a special variable for the return value of a custom block and present it as a custom reporter
Have SA create a special variable for the return value of a custom block and present it as a custom reporter
You should probably do a pr for this, to prove it's possible to do this without creating a block that only works for SA users. I assume it isn't possible though and if that's the case it won't be added.
Have SA create a special variable for the return value of a custom block and present it as a custom reporter
the thing about this is that it might not always be consistent (like if the inner block yields and another script overrides the return variable, or if the block is recursive)
I've played around with this before and made some progress: https://github.com/pufferfish101007/ScratchAddons/tree/custom-reporters2. If I remember correctly, actually displaying blocks differently to how they should be serialised isn't too difficult (see #7475), but patching the vm to make things run correctly is more of a challenge. I can't remember exactly what stage I got this to but I've probably put some progress reports in both the devlopment and (now archived) community discord servers. There are also problems with the whole return variable approach (in particular surrounding recursion and yielding); there have been semi-extensive discussions on how to mitigate these challenges effectively on the discord server; goboscript takes the approach of generating a new procedure with a different return variable for every call site, which does work but massively increases code size.
edit: updated branch to one that isn't 845 commits behind master
Using 2 blocks, set [return value] to ... and stop this script, to do the same thing seems easy enough.
Oh, I forgot recursion. With recursion it seems impossible to implement. A way would be to use a list as a stack for recursive calls. As for some other script modifying the variable while the block is running, that's why I said it should be hidden, its name would also contain the hat ID making it impossible to conflict.
I am working on this in #8674; I thought I should post here to explain the approach I plan to take so that any interested parties can give feedback if they so wish.
Using a single return variable for each procedure has the obvious downside of not supporting >1st order recursive functions (such as the fibonacci numbers as typically implemented). You can do a bit better with pushing to a list and accessing the last (or penultimate) element when you want to access the return value, but this still has problems caused by unpredictable yielding points (due to the warp timer or just user interaction) - consider the following script:
This would transpile to
You can hopefully see that the execution order is nondeterministic and so you'd get inconsistent results.
A solution that avoids all problems with yielding and warp timer issues was proposed by @Tacodiva:
- Prepopulate a return list with empty items, one for each non-procedure hat block in the sprite - for now say that we share one return list for all reporters
- For every custom reporter call, assign a static index into the return list
- Add an extra argument (hidden for SA users) to the custom procedure for the list index
- To return, set the item in the return list corresponding to the index that was passed in
- If there are multiple calls of the same reporter in the inputs of one stack block, then:
-
- The first call is as described above
-
- The second call is moved into an auxiliary procedure which takes the same index as the previous call, as well as the return value of the previous call; the outer stack block is moved into this auxiliary procedure so it can utilise both return values
-
- Repeat recursively with as many auxiliary procedures as required, passing in all previous return values to each auxiliary procedure (optimisations could also be made here to pass in relevant calculations combinging previous return values).
This transforms
into
Obviously this is not necessarily particularly nice to read, nor is it terribly intuitive, but it does work. It's also not total madness; it's essentially continuation-passing style.
There are however many cases in which this can be simplified. If it can be proven that a reporter is used in a call chain with no recursion, the auxiliary functions become unnecessary and we simply need to provide indices into the return list that are generated from the index of the current function, if not top-level, and ensure that the return list has sufficiently many elements, and that they don't overlap.
For instance, the above example could be transpiled into
or even
which I think is quite natural to read, and not unreasonable to be expected to write manually.
Rather than having a single list for all returns, there could be one list for each reporter; I am undecided if one is better than the other.
Note: there is an implied 'stop this script' block where each of the returns are in this comment, but I forgot to add them in. woops.
The caveat to the outlined approach is that you can end up running the same script multiple time simultaneously by clicking on it in the editor and then triggering the event normally, but I think that's the only time breakage can happen.
Feedback on whether people think this is a good approach, or could be improved (whether in technical or more ergonomic terms) would be welcome.
Just wondering: can anyone identify one Scratch project that uses recursion? I wonder if it's even worth it to allow it.
Just wondering: can anyone identify one Scratch project that uses recursion? I wonder if it's even worth it to allow it.
https://scratch.mit.edu/projects/429631438/editor/
Well, technically it uses recursion - but not to compute a value, but to draw on the screen.
Well, technically it uses recursion - but not to compute a value, but to draw on the screen.
This? https://scratch.mit.edu/projects/17087436/editor/
Just wondering: can anyone identify one Scratch project that uses recursion? I wonder if it's even worth it to allow it.
Many recursive algorithms return a value. Off the top of my head;
- Binary search is easily implemented using recursion
- Iterating recursive data structures (eg, searching a file system with folders)
- Recursive expression parsing
I think that the reason projects don't do it much currently is because it's not ergonomic to do so. If custom reporters were added, I think using recursion to calculate values would become a lot more appealing.
I think recursion gets a bit of a bad name from examples like the Fibonacci sequence or calculating factorial where the recursion is kinda silly and weird. There are definitely good real world applications of recursion though.