Pbindef ( append | prepend | randomize | replace ) keys?

Hi there,

Take the following minimal code :

Pdef(\test, Pbind(
	\a, 1,
	\b, Pfunc({|e|e.postln;}),
	\c, Pkey(\a).trace,
)).play;

It displays as expected a infinite series of :

( 'a': 1 )
1

Then, with the above still running, try :

Pbindef(\test, 
	\b, Pfunc({|e|e.postln;}),
	\c, Pkey(\a).trace,
);

and then inspect the Pdef with :
Pdef(\test).source.postcs;
You can see that the key order is :
PbindProxy('b', Pfunc({|e|e.postln;}), 'c', Pkey('a'), 'a', 1)
with a at the bottom rendering an empty environment for Pfunc and nil for Pkey(\a).

If this is not expected, should I file an issue ?

Can Pbindef append rather than prepend the keys ?

Thanks for any pointers on the subject :smiley:

Since a Pbind does have an expected order of keys stored as a SequenceableCollection (Provided I read and understood the source code correctly) it would then be expected that Pbindef replace keys at the exact position it was before in the Pbind.

Opinions ? (Or just a pong? I feel so alone in this conversation :pleading_face:)

Also, then, in the Pbindef, could one choose where the key(s) be inserted in the sequence of keys of the PbindProxy ?

  • If key is already there in the Pbind → replace at position.
  • If its a new key → have some sort of mechanism to (prepend | append) with an optional argument being a \key (that could be expected to already be in the Pbind.
    • prepend() → add key to the top (the current behavior)
    • append() → add key to the bottom
    • prepend(\a) → add key exactly before \a
    • append(\a) → add key exactly after \a

Inb4; This is just a concept, not actual code… :roll_eyes:

I think it’s worth filing a bug about this. I think it’s reasonable to assume that new keys given to Pbindef should override the old values and not disrupt the order, but the current logic when converting a Pdef to Pbindef is to put the new pairs first. The place to intervene would be here: supercollider/SCClassLibrary/JITLib/Patterns/Pdef.sc at develop · supercollider/supercollider · GitHub

(OTOH… in your code, why not start with Pbindef, instead of converting?)

The following doesn’t break the order, but introduces an unused pair:

(
Pdef(\test, Pbind(
	\a, 1,
	\b, Pfunc({|e|e.postln;}),
	\c, Pkey(\a).trace,
	\type, \rest
)).play;
)

// convert to Pbindef
Pbindef(\test, \dummy, 0);

Pbindef(\test, 
	\b, Pfunc({|e|e.postln;}),
	\c, Pkey(\a).trace,
);

… and users shouldn’t have to do that.

hjh

Here’s a possible fix (though the actual JITLib maintainers may have other suggestions):

Pbindef : Pdef {
	*new { arg key ... pairs;
		var pat, src, newPairs, pairOrder;
		pat = super.new(key);
		src = pat.source;
		if(pairs.isEmpty.not) {
			if(src.class === PbindProxy) {
				src.set(*pairs);
				pat.wakeUp;
			} {
				if(src.isKindOf(Pbind))
				{
					pairOrder = pairs[0, 2..];
					newPairs = pairs.asDict(class: IdentityDictionary);
					pairs = src.patternpairs.copy;
					pairs.pairsDo { |key, pat, i|
						if(newPairs[key].notNil) {
							pairs[i+1] = newPairs[key];
							newPairs.removeAt(key);
							pairOrder.remove(key);
						}
					};
					pairOrder.do { |key|
						pairs = pairs.add(key).add(newPairs[key]);
					};
				};

				src = PbindProxy.new(*pairs).quant_(pat.quant);
				pat.source = src
			};
		};

		^pat

	}

	...
}

hjh

1 Like

Wow, thank you so much @jamshark70, it works like a charm :smiley: !

Kudos for the pairs[0, 2..] syntax e.g.:

x = [\a, 0, \b, 1, \c, 2];
x.copySeries(0, 2)
==
x[0, 2..];

I had never seen that before.

Thank you! I think the whole thing was just backward. For me, this fixes it, could you pleae check if it does so for you?

*new { arg key ... pairs;
		var pat, src, newPairs;
		pat = super.new(key);
		src = pat.source;
		if(pairs.isEmpty.not) {
			if(src.class === PbindProxy) {
				src.set(*pairs);
				pat.wakeUp;
			} {
				if(src.isKindOf(Pbind))
				{
					newPairs = src.patternpairs.copy;
					pairs.pairsDo { |key, pat|
						if(newPairs.includes(key).not) {
							newPairs = newPairs.add(key);
							newPairs = newPairs.add(pat);
						}
					}
				};

				src = PbindProxy.new(*newPairs).quant_(pat.quant);
				pat.source = src
			};
		};

		^pat

	}

Will this version update keys that existed in the original Pbind and which are re-specified in the new pairs given to Pbindef?

hjh

Ah yes, no, that is not the case.

Much more uniformly, we can probably just write:

*new { arg key ... pairs;
		var pat, src, oldPairs;
		pat = super.new(key);
		src = pat.source;
		if(pairs.isEmpty.not) {
			if(src.isKindOf(PbindProxy)) {
				src.set(*pairs);
				pat.wakeUp;
			} {
				if(src.isKindOf(Pbind)) {
					src = PbindProxy(*src.patternpairs.copy).set(*pairs);
				} {
					src = PbindProxy(*pairs);
				};
				pat.source = src;
			};
			^pat

		}
	}

(updated, not fully tested)