Report inside ring leaking out of scope when run (not called)

steps to reproduce:

  1. create a command block that runs its argument:

  2. create a ring with a reporter and pass it to the block, then try to do something else after:

expected behaviour: the script runs to completion and says "this doesn't", or an error is thrown along the lines of "result of reporter ignored"

actual behaviour: the script stops after run arg and never reaches the last block


  • this only happens with rings, not custom blocks (if you pass a ring with a custom block to the run arg block it runs normally, like in this example where foo just reports a number, it runs the whole script. although that might be because of the extra indirection from the ring around foo?)

  • using single-stepping shows that the script itself ends up reporting what the ring was supposed to:

this is why i called it "leaking out of scope". it's as if i wrote this in c:

#include <stdio.h>
int foo() {
  return 1;
int main() {
  return 0;

and the program exited with code 1 and didn't print two (obviously wrong)

AFAIR it's just a design decision to remove the ring when a variable is dropped onto the slot as that would be the most probable thing to do

Which it isn't in some cases (such as yours) - solution is to just ringify manually

yeah, i know that the ring falls off variables like that, but that's not where the bug is (unless i'm being seriously ignorant right now, apologies if i am). passing the variable in a ring to run arg would run the variable block itself, rather than the ring inside the variable. i want it to be unringified because i'm passing the contents of the variable to run arg, the bug is that the report inside the ring (which is inside the variable) ends up reporting from the script that runs it (which is very outside the ring's scope) if it's run rather than called (e.g. because i want to discard the reported value)

for context, this showed up in a script i was using to test my "reader" based on manual section viii.d (OOP with Procedures):

make a reader of returns a ring with a "dispatch procedure", so that calling it with the name of a method runs the corresponding code. calling it with input "read" advances the reader and reports the next item, but i wanted a command block that advances the reader without reporting anything, so i assumed i could just run it instead of calling it. imagine my confusion when my test script just stopped after the advance (a) block with no explanation

Sorry - this sort of stuff is above my pay grade :slight_smile:

naw, this is totally the intended behavior for report, otherwise you wouldn't be able to write custom reporters.

okay, if this is really intended behaviour, maybe it would be worth adding some documentation footnote that running a ring that reports will stop the script that runs it (even from inside another block)? otherwise it's pretty opaque and hard to debug when a script that has no report or stop blocks in it just stops halfway through

Maybe this could be made clearer in the manual, but the reason we have both CALL and RUN primitives is that you're supposed to CALL expressions (headed with reporters) but RUN instructions/scripts (headed with commands).

i understand that, but if i want to run a reporter for its side effect but discard the reported value, i would expect run to do that, since it (on the surface at least) seems to do the same thing as call except it's a command shape. presumably instead i should create some block like this (deliberately with no definition):
so that i can use it like this:
this stops the behaviour of reporting from the script that calls the block

either way i don't think the behaviour expected by any random user is for their script to stop because of a report block being run instead of called inside of another block. custom blocks are fairly analogous to functions from other languages, where you generally can't return from the function that called it (without some janky reflection/metaprogramming anyway), so i'd question whether the edge case of running a reporter ring should violate that expectation?

jens says "otherwise you wouldn't be able to write custom reporters" but i think both of these cases could still work:
without allowing this somewhat questionable case to work (as it does currently):
(try it if you want)

imagine some dodgy library author makes a blocks module with some block like this:
and a user uses it in a reporter like this (without first checking the definition of that block):
this really doesn't seem like it "works as intended" to me:

sorry for such a long post i just really want to make it clear what my point is and i figured a real world example might be a better way to do that

Thank you for your detailed exposition.

I'm not sure what Jens meant by "works as intended." But I do know there are many places in Snap! where not all possible errors are caught, because the checking for errors would slow down correct programs as well as erroneous ones. RUNning a reporter is an error; the domain of RUN is scripts, not expressions. (We do try hard to give correct answers for correct programs, of course.)

I agree that the particular response you've seen to this particular error is surprising. Even for correct programs, it was hard for us to get this right in every case. For example, suppose you're writing a custom reporter, and inside it you use a FOR loop, and inside that you use a REPORT. What can you possibly mean? We think you can only mean to report from your custom reporter block, because FOR is a command and you can't report from it. But it's not so easy to implement that; we have to unwind the stack until we reach (a call to) a reporter. This was especially tricky when FOR was itself a custom block, in the BYOB days, and it took a while for us to handle all the edge cases. This FOR-inside-reporter case is probably what leads to the behavior you're complaining about. But that's a guess; I haven't traced through the interpreter to be sure.