PnNilSafe : Pn

It is possible to hang SuperCollider by using Pn and Plazy together, under the following conditions:

The number of repeats for Pn is inf.

Plazy infinitely returns nil for any reason.

For instance,

// do not execute this! Unless you want to force quit SuperCollider

p = Pn(Plazy({ nil }), inf).asStream;

p.next;

Obviously, in a perfect world, Plazy should never return nil, but we make mistakes and the risk of error here requires a complete restart of SuperCollider, which naturally we want to avoid.

For event patterns, PnNilSafe behaves identically to Pn except that it terminates immediately in the above case.

// you may execute this

p = PnNilSafe(Plazy({ nil }), inf).asStream;

p.next;

It does this by checking the logical time when it embeds the repeated pattern into the stream. If it's the same time as the previous embedding, this means that the child stream yielded no values and the stream terminates. (The problem with Pn is that if the pattern to be repeated returns without yielding a value, it will keep repeating the empty stream instead of returning.)

This is intended for streams that will be scheduled on a clock -- any event pattern or stream, or a value pattern used within a Pbind. Be aware, though, that the stream may terminate prematurely if the event pattern returns a zero delta. Exercise caution when using PnNilSafe with Ppar or Ptpar, for instance, or to play chords using zero deltas.

// early termination example

p = Pn(Plazy({ Pshuf((1..4), 1) }), inf).asStream;

p.nextN(10);

[ 3, 1, 4, 2, 2, 3, 1, 4, 4, 3 ]

p = PnNilSafe(Plazy({ Pshuf((1..4), 1) }), inf).asStream;

p.nextN(10);

[ 2, 3, 4, 1, nil, nil, nil, nil, nil, nil ]

Because nextN requests all of its values at the same logical time, PnNilSafe exits to prevent the dangerous infinite loop.