firstArg question

Here’s what I want to do:

~function.(3, 3, 3 /* synth plays NOW */)

this works:

~function.( * ( ([3, 3, 3]) <! (7.postln) ) )

but this doesn’t:

~function.( * ( ([3, 3, 3]) <! (Synth(\default) ) ) )

Is this just not possible?

There’s only one documented instance firstArg in Symoblic Notations… so maybe there’s something I’m missing.

Over the years, I came to feel pretty strongly that a clear and direct coding style is better in the long run than a terse, clever coding style. For a simple example, I used to write things like

if((variable = expression).notNil) {
	do something with variable
}

… because it would save a bytecode to access the variable. Now, I would rather write it like this:

variable = expression;
if(variable.notNil) {
	do something with variable
}

It’s slightly more verbose, but it’s also absolutely clear what is intended. Readability helps a lot when you come back to the code later.

When I read your third line, I have no idea what is the order of operations that you expect. The code doesn’t communicate well :laughing: and I find myself, to be honest, unable to answer the question of whether it’s possible or not because I don’t even know what is the “it” that may or may not be possible.

So, I suspect here that the solution is to write the order of operations that you mean, and avoid unnecessary cleverness.

firstArg is intended particularly for unit generators, when you want to force one UGen to come earlier than another in the graph, but you don’t need this UGen’s output, e.g.:

var sig = SinOsc.ar;
var phase = Phasor.ar(0, 1, 0, BufFrames.kr(buf));
var writer = BufWr.ar(sig, buf, phase);
var reader = BufRd.ar(1, buf, phase - 44100, loop: 1);

In this case, it’s possible, even likely, that BufWr might end up after BufRd. To force the writer first, it should be an input to reader but the reader doesn’t need BufWr’s output. So:

var sig = SinOsc.ar;
var phase = Phasor.ar(0, 1, 0, BufFrames.kr(buf));
var writer = BufWr.ar(sig, buf, phase);
var reader = BufRd.ar(1, buf <! writer, phase - 44100, loop: 1);

Now reader depends on a <! binary op, which depends on BufWr, so BufWr must be sorted first.

Since you aren’t doing that in this case, probably firstArg is not doing anything useful, and there should be a simpler way to write it.

hjh

1 Like

I respect the note on improve design… I almost felt it coming.

From an outside perspective, the code seems esoteric… it’s hard to know exactly what I’m trying to do.

I was more interested in firstArg… execute the second, return the first. It seems brilliant.

For some reason, there’s a limit on the execution type that I can’t fathom…

My ultimate intent, in this, is focused on aesthetics… I can easily call the synth, use a semicolon, and call the function (a sysex message), separately.

The synth, and the sysex messasge, are both for the same destination, in a 3 dimensional key-value matrix… it’s, in this case… and perhaps unexpectedly, given how contrived the example appeared to be… and without argueing about design, but it seems actually smoother, or at least smooth enough, in terms of design, to:

~sysex(3, 1, 3, <! Synth) // <- there!

…which I suspect I’m still unable to do, so perhaps it can’t be done.

I would love it firstArg could accept { brackets }… it comes from AbstractFunction.

To be accurate, it’s really “evaluate the first; evaluate the second; return the first.” (I’m saying “evaluate” rather than “execute” because of the next part…)

Ohhh… now I remember. firstArg is implemented as a math operator.

I had never really looked at it carefully, but it turns out that it does follow SC’s general rules for applying math operators to collections:

// A.
[1, 2] + 8
-> [ 9, 10 ]

// B. Well, that's clear enough
[1, 2] firstArg: 8
-> [ 1, 2 ]

// C. But it actually does [1 firstArg: 8, 2 firstArg: 8]
a = [1, 2];
b = a firstArg: 8;
a === b  // false!

// D.
8 firstArg: [1, 2]
-> [ 8, 8 ]  // ah, you didn't expect that, did you?

C and D should be enough to establish that firstArg is not a general-purpose flow of control keyword. (D, in particular, you might question – but I think it’s necessary in the case of UGen multichannel expansion.)

TL;DR Don’t use firstArg in the way you’re trying to use it here.

IMO:

~function.( * ( ([3, 3, 3]) <! (Synth(\default) ) ) )

// vs

var array;

...

array = [3, 3, 3];
Synth(\default);
~function.(array);

When you come back to your code 3 years later, your reaction to the first line is likely to be “What the ---- was I thinking?” while the second block is crystal clear.

If you insist, then the real requirement is to evaluate a number of expressions, but return (or use) only the first.

[
	[3, 3, 3],
	Synth(\default)
].first

But if that seems awkward, it’s because… it is.

hjh

…a math operator!

thank you.

I recently discovered you can pass controls to a Synth even if they don’t exist, without any errors… or even a warning.

So instead of putting the synth within the call to a sysex function, I can simply run the sysex line from within the synth, as it’s final ‘argument’… it keeps the execution function for an instrument looking how I want it to look, in this case… the unorthodox approach works in this situation in particular.

Thanks for taking the time to look into it… I also discovered that while <! is undocumented for AbstractFunction and Object, it’s not only included in Symbolic Notations… but in Operators as well… where it explains the specific Ugen output behavior you were referring to earlier.

I found <! deferring to firstArg which is not implemented by some classes quite confusing. For instance

1 ; 2   // -> 2
1 <! 2  // -> 1

true ; 2  // 2
true <! 2 // ERROR: Message 'firstArg' not understood.

From your explanation, I’m guessing it was only intended to be implemented by classes whose values can be passed around on the server, like numbers, but not Booleans.

Well, even more confusing perhaps, you can set anything on nil, without error:

nil.set(\something, 44)

whereas nil does throw error on most methods, most famously perhaps at.