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.

hjh

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.

hjh

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.

hjh

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.)

hjh