What does # mean?

I am currently reading the document for “Function” and looking at this particular example where
without the “#” there is silence. Could anyone explain why?

{ arg freq = #[ 100, 300, 900, 2700 ]; Mix(SinOsc.ar(freq, 0, 0.1)) }.play;

And also just curious what the classification is for a symbol such as “#”, because I could not find any info about it.

I am currently reading the document for “Function” and looking at this particular example where
without the “#” there is silence. Could anyone explain why?

{ arg freq = #[ 100, 300, 900, 2700 ]; Mix(SinOsc.ar(freq, 0, 0.1)) }.play;

And also just curious what the classification is for a symbol such as “#”, because I could not find any info about it.

A quick search startpage.com brought up this helpful explanation:
In supercollider, what's the role of "*", "#", "_"? - Stack Overflow

The relevant part of the function help file (although you wouldn’t know it) is

[1] Literal argument defaults

Argument defaults that are literals are stored as part of the FunctionDef.

[2] Expression argument defaults

Since expressions are evaluated when the function is called, they cannot be stored in the FunctionDef.

When building the SynthDef the arguments from the FunctionDef are used as control inputs.

The # makes the array into a literal (see Symbolic Notations | SuperCollider 3.12.2 Help), which means it is stored as part of the FunctionDef and then incorporated as control inputs to the SynthDef

Alternative that doesn’t require a literal array is to use this syntax inside your synth function (see NamedControl | SuperCollider 3.12.2 Help)

var freq = \freq.kr([100, 300, 900, 2700]);
2 Likes

@David_Ko welcome to the forum!

As you’ve experienced from the silence you got in your post, you can’t pass a dynamic array into a function as an argument. You can (and should) define an array literal as an argument default since it will be created at compile time if you absolutely must pass an array as an argument.

f = { |x = #[1, 2, 3]| x.sum };
g = { |x = [1, 2, 3]| x.sum }; 

f.value(); // returns 6

g.value(); // error

// Using different arrays as the argument to f will still work:
f.value([1, 1, 1]); // -> 3, got to be good looking cuz he's so hard to see
f.value([12, 3, 5, 6, 7]); // -> 33; still works even though this array is a different size
f.value(3.collect{1000.rand.postln}); // also still works and will be the sum of the three posted integers

Some UGens like Klank take an array reference as an argument, so if you want to supply an arrayed argument as in the helpfile examples, it needs to be a literal (see the documentation in the link). It won’t compile otherwise.

You can also use Names in literal arrays whether or not they are defined. You have to define them to use them in a dynamic array.

#[inky, pinky, blinky, clyde]; // they're all symbols
[inky, pinky, blinky, clyde]; // won't work in this block

I can’t think of a time when this is practical though. Mixing names in a block of code where some names might not be variables is bad design.

Many people on the forum advocate for using NamedControl instead of arg because you can use an array as the control value and it will still work (there are a handful of other reasons). It’s really just personal preference though.

See also: Literals

1 Like

Thanks for this response!

Maybe this is one of the weird quirks of an old language like SC? Passing around arrays without hassle/workarounds is pretty standard stuff in the modern languages I’m familiar with. But then again, even the If statement format in SC is pretty oddball hehe.

No, it’s just stated incorrectly.

You can pass any array into a function, on the client side – no problem with array passing.

The problem is that in a SynthDef, function-call passing of objects is not the right model. Looking at them as function arguments leads to misunderstanding.

In a SynthDef, there are no arguments, only control inputs, and control inputs do not behave exactly the same as function arguments.

There have been a few recommendations in this thread to create array-style control inputs using NamedControl (or its shortcut syntax, \name.kr(array)). This recommendation is correct IMO, because it shows what is really happening, and it also supports arrays that are given as an expression, not only as a literal.

What hasn’t been clear in the thread so far is that the SynthDef builder uses SynthDef function arguments to create control inputs automatically. They look like function arguments, but they are not behaving as function arguments. The format of the control input depends on the hardcoded default value for the corresponding argument in the function. This is where the literal array requirement comes from – because it must be known that the default is an array without executing anything inside the function, and this is not possible if the default is an expression.

My suggestion is to use NamedControl for all arrayed control inputs, always, and then the literal-array distinction disappears.

f = { |x|
    "x = %\n".postf(x);
};

f.([1, 2]);

// prints:
x = [ 1, 2 ];

I don’t quite see what’s non-standard about that.

if(this.isOddball) {
    "I'll eat my hat"
} {
    "I'll go on coding happily"
}

hjh

2 Likes