Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rest handling with patterns #4

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions rfcs/0004-Easier-variable-length-rests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
- Title: Easier variable-length rests in patterns
- Date proposed: 2019-11-01
- RFC PR: https://github.com/supercollider/rfcs/pull/0004

# Summary

Currently it is easy to embed a rest of a fixed value in a sequence: `Pseq([0, Rest(1), 2])`.

It is not easy to embed a rest with being lazy-calculation value: `Pseq([0, Pwhite(1, 3, 1).collect(Rest(_)), 2])`.

# Motivation

This question keeps coming up repeatedly on the mailing list and forum. It's a legitimate user need.

Users often expect to be able to write `Rest(Pwhite(1, 3))` and have the Rest be automatically stream-ified. This might be a reasonable expectation (and not hard to implement). But there is also precedent to reject that approach, e.g., an array of patterns requires a `Ptuple()` wrapper.

Alternately, we could follow the model of `.collect` -> Pcollect, `.select` -> Pselect, `.keep` -> Pfin and provide a .rest method for patterns and other objects.

Some prior discussion is here.

https://scsynth.org/t/pbind-and-samples-with-different-durations-2/735/13

# Specification

1. A `Prest(patternOrStream, n)` pattern, to take `n` values from the source pattern and output `Rest()` objects with those values -- following the model of Pcollect to implement .collect, etc.
a. Default n should be 1. See unresolved questions.

2. A `.rest(n)` method that returns `Prest(this, n)` for patterns, and `Rest(this)` or other objects. Possibly, for SequenceableCollections, we might do `this.collect(Rest(_))`.
- `2.rest` --> `Rest(2)`
- `Pwhite(1, 3, inf)` --> `Prest(Pwhite(1, 3, inf), 1)`
- (Maybe?) `[1, 2].rest` --> `[Rest(1), Rest(2)]` but here, I don't have a concrete use case. Currently the relationship between rests and arrays is undefined.

# Drawbacks

A possible objection is "too many ways to write the same thing." In fact, I raised that objection to a `.rest` method in the past. But we have both `.fin` and `.keep` as pattern aliases for `Pfin`, so there is precedent and I've relaxed my objection.

# Unresolved Questions

### `.rest` is not precisely analogous to `.collect`.

In `pattern.collect(...)`, we expect every value of the pattern to be streamed out and further mapped by the function.

In `pattern.rest(...)`, it's pointless to stream out all the values and turn them into rests, because then you will have a potentially long series of silent events.

`Pbind(\degree, Pwhite(0, 7, inf), \dur, Pseq([0.5, 0.5, rest(Pwhite(1, 4, inf) * 0.5)], inf))` -- if we follow the `collect` model, then you get two notes and then silence, indefinitely.

I believe the best solution is to limit the length of the Prest stream, default = 1. But this is worth discussing.

### (xyz).rest vs rest(xyz) vs Rest(xyz)

Note here that I decided to save a dot by using function-style notation: `rest(Pwhite(1, 4, inf) * 0.5)`.

If we support this, then inevitably -- guaranteed -- some user will write `Rest(Pwhite(1, 4, inf) * 0.5)` and get annoyed that it doesn't work.

I think it wouldn't be hard to fold Prest behavior into Rest:embedInStream. Is this worth doing for user friendliness, or should we be strict and enforce the distinction between a Rest object (simple value) and the rest method applied to patterns?