Technically there’s nothing that can’t be programmed with the primitives exposed by Sonic Pi, but sometimes it seems like it’d be nice to have an iterator API that you could 1. layer over the persistent immutable data structures you have today and 2. implement for your own iterators that aren’t necessarily based on an immutable data structure.
Specifically, when I’m messing around with rhythms, i usually have something like
rhythm_a = spread(1, 4)
rhythm_b = spread(3, 8)
rhythm = rhythm_a # this can be changed live
melody = (scale :c3, :major)
play melody.tick(:melody) if rhythm.tick(:rhythm)
Now, for the purposes of live coding (and really any coding), it’d be ideal if I could just swap the rhythm or melody by pointing it to another data structure / object / thing; this of course works if everything can be represented with rings or other indexable data structures, but what if I wanted to do something weird like generate a melody based on the fibonacci sequence or something that can be lazily constructed but isn’t really indexable?
I think maybe what’s missing from Sonic Pi (and I totally understand if it’s not worth adding for reasons of simplicity / ease / minimal API surface) is an iterator API. I get that you’re trying to avoid mutation and preserve the composability of immutable persistent data structures, but iterating through these structures already involves mutating this weird ambient mutable counter known as tick.
If Sonic Pi had an iterator API, I could rewrite the code above to always use iterators that internally “tick” whenever you call
.next on them. Instead of
tick_reset, you create a new iterator based on the same immutable datastructure. And then if on top of that you could define your own algorithm iterators that implement the
.next interface, then there’d be no limit to the creativity.
But I totally understand the constraint to keep the API surface minimal; though I’m loving the design of SuperCollider’s Sequence APIs, the API surface of SC is preeeeeetty overwhelming.