Function with rest of arguments

How do I define a function which gets some positional arguments and a whole rest of arguments. This is not working:

(
{arg x, ...rest; [x, rest].postln}.(1,2,3,4,5)
)

I had expected it to answer [1, [2, 3, 4, 5]]

Because the ... is already a separator between tokens, the comma would be redundant and should be left out.

(
{ arg x ... rest; [x, rest].postln }.(1,2,3,4,5)
)

I was looking for where this is documented andā€¦ canā€™t find it just nowā€¦

EDIT: OK, yeah, this is confusing: ā€œIf the last argument in the list is preceded by three dots (an ellipsis), then all the remaining arguments that were passed will be assigned to that variable as an Array. Arguments must be separated by commasā€ when itā€™s really ā€œArguments must be separated by commas except when using ...ā€ ā€“ Like in your writing classes in school, ā€œA paragraph should have only one topic sentenceā€ ā€“ this one switches topics and led to a wrong conclusion that ā€œ,ā€ is required for ... args.

So maybe change like this:

Arguments must be separated by commas.**

If the last argument in the list is preceded by three dots (an ellipsis), then all the remaining arguments that were passed will be assigned to that variable as an Array. In this case only, the comma should be omitted.

** (Except that isnā€™t true either: { |x 1 y 2| [x, y].postln }.value)

hjh

1 Like

ā€¦ and `{| a b ā€¦ args| } doesnā€™t need commas either!

how about:

An argument list immediately follows the open curly bracket of a function definition. An argument list either begins with the reserved word arg, or is contained between two vertical bars. When using the ā€˜argā€™ style arguments must be separated by commas, when using vertical bars (ā€˜pipe modeā€™) commas are optional.
[ ā€¦ ]
If the last argument in the list is preceded by three dots (an ellipsis), then all the remaining arguments that were passed will be assigned to that variable as an Array. Ellipses should not be preceded by a comma.

Slightly off topicā€¦

Was just looking at the bison file to see how this was defined and realised a whole bunch of stuff.
This is all validā€¦

Modify existing (right to left) arguments

{
	|foo(#car, bar = gar.collect(_+1)), bar, car ...gar|
	[foo, bar, car, gar].postln
}.(gar: [1,2,3]) // [ [ 2, 3, 4 ], 3, 2, [ 1, 2, 3 ] ]

Thowing if an arg isnā€™t set (perhaps legitimately useful?)

~foo = {
	|a( Error("arg not set").throw )|
	a + 1
};
~foo.(); // throws
~foo.(1); // 2

Mutating globals.

~c = 10;
~m = { var out = ~c; ~c = ~c - 1; out };

~a = { |a( ~m.() )| a  };
~a.() // 10
~a.() // 9
~a.() // 8
....

Multiple expressions and blocking.

~a = {
	|a( 1.wait; \bar )|
	a.postln 
}

Any valid expression can go in there, including, my favouriteā€¦

{ |head(thisFunction.value)| }.()   // (this will crash the interpreter)

Also, when using pipes (which is called slotdef in the code) there can can only have literals after the equals sign but any expression in brackets, whereas with arg you can have any expression after the equals.

ok this is nuts - can you walk me through this? when did car get set to 2?

If thereā€™s arg a = expression or | a(expression) |, it compiles the same as arg a = nil; a !? { a = expression }.

So this example is really:

{
    |foo, bar, car ...gar|
    foo !? { foo = (#car, bar = gar.collect(_+1)) };
    [foo, bar, car, gar].postln
}.(gar: [1,2,3]);

EDIT: I forgot the variable reassignment initially, fixed.

Iā€™ve doubts about readability but itā€™s interesting that it can be pushed this far.

hjh

Yeah, the arg list syntax and semantics are pretty crusty when you consider all the cases :smiley: I donā€™t think anyone makes too significant use of it.

This is one of the more delightfully weird corners of the sclang syntax. But, consider that this partly what saves us from the fate of Python, where the default values for function arguments are actually globals that can be mutatedā€¦

As soon as you can have any default that isnā€™t a literal (essential for sclang because lots of basic things arenā€™t literalsā€¦), youā€™re a bit stuck allowing any expression as a default and executing it more or less like this - or, disallowing ā€œweirdā€ cases in semi-arbitrary ways. I think the semantics and allowances here are pretty close to C++, except in C++ the defaults are evaluated at the call site so the other arguments arenā€™t in scope to use / mutate.

It used to be possible to mutate String literal argument defaults :astonished: until we made String literals immutable to fix that :laughing:

hjh