Supercollider: The help/documentation for "while" should state that the while-expression itself evaluates to nil

Created on 12 Mar 2020  路  3Comments  路  Source: supercollider/supercollider

Environment

  • SuperCollider version: 3.11

Expected vs. actual behavior

The SC documentation on control structures (Control-Structures.schelp) is currently silent on this, but SC seems to use the Smalltalk inspired idea that the while expression itself evaluates itself to nil i.e.

v = 22;
v = (x=0; {x<50}.while({x=x+1}));
v; // -> nil; v itself is nil now

This in line with Smalltalk and its other derivatives, e.g. Ruby has the same default nil as the value of a while-expression. Nevertheless I think it should be mentioned in the documentation that the value of a while-expression is itself nil in SC, since this is the implemented behavior.

Aside comment

Ruby and Smalltalk allow a while-expression's "return" value to be explicitly overridden. In Ruby this is done by a break <value-returned>, i.e. with a separate keyword. That break feature exists in Smalltalk too, albeit not as a separate keyword, but as caret itself, because "true" Smalltalk blocks act like methods in themselves. SC almost has that break behavior too, as caret "breaks" a while loop in the top-level interpreter, but that doesn't actually assign a value to the while-expression in SC, i.e.

v = 22;
v = (x=0; {x<5}.while({x=x+3; ^47}));
v; // still 22, unlike in "true" Smalltalk where it would be 47, but...
x; // is 3 because caret broke the loop in SC too


bug

All 3 comments

SC almost has that break behavior too, as caret "breaks" a while loop in the top-level interpreter, but that doesn't actually assign a value to the while-expression in SC

No! That's definitely not safe.

^ is like return in C -- but, where C applies it to any function, in SC it's only to exit and return a value from the current method. Submitting a while interactively means that the ^ is not in the scope of a method that you've defined yourself -- so you have no idea where it's returning from or where the return value is going to ("undefined behavior," which is why v didn't get the break-return value).

There's a safe way to do early exit from a loop, with a return value.

v = 22;
v = block { |break|
    x = 0;
    while { x < 5 } {
        x = x + 3;
        break.value(47);
    };
};
v; // 47
x; // 3

... which is very cleverly implemented -- pass a function containing the method-return opcode -- this opcode is in the context of the block method, hence it returns only to the caller of block.

Yes, that's indeed a good tip on the SC-way to do a break, which could be mentioned in the help file on "Control Structures" as well... in addition to stating that a while-expression evaluates to nil in SC, which the current documentation/help on "Control Structures" (HelpSource/Reference/Control-Structures.schelp) doesn't say.

Fixing that small but non-trivial oversight was the main purpose I filed this bug report (hence the bolded sentence in it). For anyone diving into SC who is not already familiar with Smalltalk or Ruby, it may not be obvious that while a function in SC returns its last expression evaluated, a while-expression always evaluates to nil, even though both functions and while expressions look like using {} blocks.

Per your fine suggestion, Control-Structures.schelp could also mention that while there's no break keyword in SC, Function.block provides a way to achieve an equivalent. The help for Function.block does have an example already, so Control-Structures.schelp could just link to it.

thanks! in general, we don't keep issue tickets open for individual documentation issues -- it will just end up creating clutter in the long run, as it's already known that many things in SC are mis- or under-documented. however, that isn't to say that this isn't worth fixing. feel free to open a PR to improve the documentation!

Was this page helpful?
0 / 5 - 0 ratings