Explanation for how the server handles if()?

Hi all,

I don’t really have a problem here, just trying to understand some unexpected behaviour.

(
{
    var sevens = DC.kr(7);
    Poll.kr(Impulse.kr(60), [
        
        sevens,
        
        if(sevens == 7, 1, 0),
        if(sevens < 7, 1, 0),
        if(sevens > 7, 1, 0),
        
        if(7.0 == 7, 1, 0),
        if(sevens <= 7, 1, 0),
        if(sevens >= 7, 1, 0),
        
    ])
}.play
)

The above code polls 7 lines, which all seem to contradict each other a bit; DC.kr(7) is not equal to 7, nor is it less than or greater than 7.

Insight into what’s happening on a low level would be appreciated.

The ‘==’ operator is somehow exceptional. If you look at the implementations of ‘<’, ‘>’, ‘<=’ and ‘>=’, you see that these operators (methods) are defined for UGens via the superclass AbstractFunction. For some reason this is not the case for ‘==’, so the operator’s implementation for AbstractFunction’s superclass Object is applied and you end up with a result, which is no UGen, which breaks the ‘if’ pseudo ugen:

    if { arg trueUGen, falseUGen;
        ^(this * (trueUGen - falseUGen)) + falseUGen;
    }

If you want to make such work, you can e.g. use the (not very handsome) syntax

(
{
    var sevens = DC.kr(7);
    Poll.kr(Impulse.kr(1), [
        
        sevens,
        
        if(BinaryOpUGen('==', sevens, 7), 1, 0),
        if(sevens < 7, 1, 0),
        if(sevens > 7, 1, 0),
        
        if(sevens <= 7, 1, 0),
        if(sevens >= 7, 1, 0),
        
    ])
}.play
)

By the way the analogue problem occurs with Pif and ‘==’, you can’t use this, instead replace with Pbinop(’==’ etc. This is fooling people since … decades, I feel :slight_smile: There doesn’t seem to be an easy workaround though.

(
SynthDef(\help_sinegrain,
    { arg out=0, freq=440, sustain=0.05;
        var env;
        env = EnvGen.kr(Env.perc(0.01, sustain, 0.2), doneAction: Done.freeSelf);
        Out.ar(out, SinOsc.ar(freq, 0, env))
    }).add;
)


(
var a;
a = Pif(
    Pfunc({ [0, 1].choose } == 0),   // doesn't work
    // Pfunc({ [0, 1].choose } < 1),   // works
    // Pbinop('==', Pfunc({ [0, 1].choose }), 0), // works
    Pn(Pseries(0.5, 0.1, 10)), Pn(Pseries(6, -0.1, 10))
).asStream;
{
    loop {
        Synth(\help_sinegrain, [\freq, a.next * 600 + 300]);
        0.2.wait;
    }
}.fork;
)

And actually the original example of this help file is not ideal IMO, as it implements a syntax which leads to inexact timing, but this is another story:

http://doc.sccode.org/Guides/ServerTiming.html

1 Like

… ah, I’ve made a mistake in showing the disfunctionality, but this doesn’t change anything:

instead of

Pfunc({ [0, 1].choose } == 0),

you would want to write

Pfunc({ [0, 1].choose }) == 0,

but it doesn’t function either

Brilliant, that makes perfect sense. Thank you! I guess this is the kind of reason why === is part of JavaScript?

The BinaryOpUGen function could be useful in the future (​:

Another interesting detail: you can apply operators to Functions

{ [0, 1].choose } < 1

-> a BinaryOpFunction

as a consequence it doesn’t matter in that case if we write the operator inside or outside Pfunc’s argument !

Equivalent:

Pfunc({ [0, 1].choose } < 1).asStream.nextN(100)

(Pfunc({ [0, 1].choose }) < 1).asStream.nextN(100)

But, here too, ‘==’ isn’t defined for Function – as it isn’t for AbstractFunction – so both ‘==’ variants don’t work.