Pipe_UTIL {
*getFunc {|a|
^a.isKindOf(Symbol).if(
{ {|...b| a.applyTo(*b)} },
{ a }
)
}
}
+Object {
identityFunc { ^this } // unlike _.value, this always does nothing.
|> { |f, adverb|
^case
{adverb.isNil} {Pipe_UTIL.getFunc(f).(this)}
{adverb == \fork}{
if(f.size < 3, {Error("|>.fork must have an array of 3 or more functions").throw});
f[1..].collect{|o| Pipe_UTIL.getFunc(o).(this) }
.reduce(Pipe_UTIL.getFunc(f[0]))
}
{adverb == \tap} { Pipe_UTIL.getFunc(f).(this); this }
}
}
+Function {
|> { |f, adverb|
^case
{adverb.isNil} {
{ |...a| Pipe_UTIL.getFunc(f).(this.(*a)) }
}
{adverb == \fork}{
if(f.size < 3, {Error("|>.fork must have an array of 3 or more functions").throw});
{ |...a|
f[1..].collect{|o| Pipe_UTIL.getFunc(o).(this.(*a)) }
.reduce(Pipe_UTIL.getFunc(f[0]))
}
}
{adverb == \tap} {
{ |...a|
var r = this.(*a);
Pipe_UTIL.getFunc(f).(r);
r
}
}
}
}
So here is an exampleâŚ
SynthDef(\pipes, {
WhiteNoise.ar()
|>.fork ['+',
_.identityFunc,
BHiShelf.ar(_, 5000, 4) |> _.tanh
BLowShelf.ar(_, 500, 4) |> _.tanh
]
|>.tap ReplaceOut.ar(\rout.kr, _)
|> (_ * -10.dbamp) |> _.tanh
|> Out.ar(\out.kr, _)
})
SynthDef(\noPipes, {
var sig = WhiteNoise.ar();
var sig2 = sig + (BHiShelf.ar(sig, 5000, 4).tanh) + (BLowShelf.ar(sig, 500, 4).tanh);
var attenuatedSig = (sig * -10.dbamp).tanh;
ReplaceOut.ar(\rout.kr, sig2);
Out.ar(\out.kr, attenuatedSig);
})
A few things to noteâŚ
It is often better to write (_ + 1)
rather than just + 1
as the latter does not work when you are building a chain of Functions, but does work if you are immediately applying them.
f = (_ * 2) |> (_ + 1);
g = (_ * 2) |> _ + 1;
f.(1) // returns 3
g.(1) // returns a BinaryOpFunction
|>.fork
takes the combining function as its first array element, but there must be more than 2.
Internally it uses reduce
to combine them.
When using function composition in this way, some of supecolliderâs more questionable design choices start to rear their heads.
One thing that always catches me is that ++
doesnât actually do array concatenation (I blame String for decaying into an Array) if the left hand side isnât already an array like thing (but nil works and so do, importantly, Ugens).
Here is the same midside conversion on numbers which donât turn into arrays.
[0.2, -0.7] |>.fork ['++', _.reduce('-') |> _.asArray, _.reduce('+') |> _.asArray]
In the past I have had many different adverbs on |>, there is a whole branch of logic call combinatory logic that deals with these sorts of operators. I think these are the only useful ones as supercollider has good functional support already â might be wrong though.