Difference between Pbindf and Pchain

Hi, I tried to search, but could not find an answer.

Is there any practical difference between Pbindf and Pchain (resp. the <> operator) for composing Pbinds?

// chain two Pbinds
~a = Pbind(\a, Pseq([1, 2, 3], inf));
~b = Pbind(\b, Pseries());
~stream = (~b <> ~a).asStream;
10.do { ~stream.next(()).postln };

// same with Pbindf
~b = Pbindf(~a, \b, Pseries());
~stream = ~b.asStream;
10.do { ~stream.next(()).postln };

I get the same results… So what’s the purpose of Pbindf?

Basically, there isn’t a specific need for it. You’re correct that Pchain is more general.

We can’t just summarily delete Pbindf though, because we have no idea who is using it.


Thanks! That’s what I figured.

We can’t just summarily delete Pbindf though, because we have no idea who is using it.

Sure. At least the documentation could mention that it is deprecated and recommend <> resp. Pchain instead.

A couple of things to consider before deprecating something that is apparently redundant:

  • Is it worth end-user irritation? (Thinking of the balance between developers’ wish to reduce maintenance load vs end-user annoyance when they have to edit their code upon upgrade.)

  • Does it help or hurt code expressivity? E.g., we “could” (almost) remove Integrator.ar in favor of FOS.ar(sig, a0: 1, a1: 0, b1: 1) but… does anyone want to read that? :wink:

  • Does the redundant class execute faster? If so, that may be a reason to keep it. (Integrator could be seen as an optimized formula, removing the unused i-1 feedforward term.)

Just questioning a little bit the assumption that “redundant class” automatically, in every case, calls for deprecation.

In this case, though (and to my surprise), Pbindf is slower than Pchain. A lot slower.

var a = Array.fill(50, { |i| [("a" ++ i).asSymbol, Pseries(0, 1, 10000)] }).flat;
var b = Array.fill(50, { |i| [("b" ++ i).asSymbol, Pseries(0, 1, 10000)] }).flat;

var chain = Pchain(Pbind(*b), Pbind(*a));
var pbf = Pbindf(Pbind(*a), *b);

c = Pn(chain, 1000).asStream;
f = Pn(pbf, 1000).asStream;

bench { c.all };
time to run: 0.095601731999977 seconds.

bench { f.all };
time to run: 0.17033853699991 seconds.

So in this case, there would at minimum be good reason to replace the current Pbindf implementation to just return a Pchain, and perhaps even to deprecate.


I didn’t really think about a formal deprecation, I just thought that the help file could explain that it does the same thing as <> resp. Pchain and recommend the latter instead.

Perhaps slightly off topic…
but does any one know why the symbol <> was chosen for this operation, when it is conceptually a concatenation of keys and functions like, (...) ++ (...), (event concat), whilst Pbind() ++ Pbind() is used to mean Pseq(Pbind(), Pbind())?

It’s analogous to function composition. In mathematics, f · g = f(g(x)) meaning, evaluate g, then pass that result to f. This is exactly how pattern composition works too.


1 Like

I’d rather see <= or even < to make order of evaluation explicit myself…

< is “less than”; <= is “less than or equal to.”

Note that f < g, when f is a function, already means “return a new function where both f and g are evaluated and the ‘less than’ result is returned.” So you can’t overload < or <= to mean function composition.

JITLib uses <<> and <>> for nodeproxy chaining. f <<> g could be borrowed for functions and patterns.

(Btw I had forgotten to mention that <> is a loose visual analog to the middle-dot operator, using standard ASCII characters. · doesn’t exist in the 0-127 code point range.)