Accumulation over patterns (why we cannot have it)?

Re: Changing sounds within Euclidean Rhythms - #9 by heavy.lifting ("great challenge")

Interesting problem! In functional reactive programming, there are accumulating functions, e.g., Reactive.Threepenny

accumB :: MonadIO m => a -> Event (a -> a) -> m (Behavior a) 

(let's assume, roughly, Event = Behaviour = Pattern and m = Identity) then we could theoretically hope for

accumB 0 (euclid 3 8 $ pure succ) 

to mean the pattern that counts 0, 1, .. and changes value exactly at the Euclidean points.

But that's hard since we could have parallel events, e.g.,

accumB 0 (stack[pure pred, pure succ])

what should happen? In this specific case, "apply in any order" is probably fine,
as pred . succ = succ . pred but for non-commutative operations?

So, this cannot exist in general? Because we'd have to allow some unspecified behaviour?

NB: a similar thing already happens with global effects (e.g., delay) that have their parameters overwritten seemingly randomly from different sub-expressions of stack.

Couldn't there be a concept of parallel continuous values?

Well, that depends on the meaning of "parallel value" that you have in mind. We do have something like that, namely, Patterns built with stack? That's parallel - but parts are independent. I mean, it's purely functional, so there are no (hidden) dependencies anyway.

I think I may not have understood the source of the problem properly.

It is a digression, I continue anyway, perhaps we can move this elsewhere later.
Let's say you layer some patterns:

let p = stack ["hh", "cp"]

is there a way to use p and apply one function to the first layer and another to the second?

Good question! I think the answer is NO, since the concept of "first" (layer) and "second" does not exist (and cannot be realized) for the type Pattern a (which just denotes a set of events).

I thought that's one motivation for the type Sequence a in tidal-2. (Definition of type: Tidal/Types.hs at cycseq · tidalcycles/Tidal · GitHub operations for the type: Tidal/Sequence.hs at cycseq · tidalcycles/Tidal · GitHub) There, (some) structure is kept, so we can later work on it.

Hm, isn't Sequence a sequence in time? But yes, seems similar. In sclang Ppar there is a similiar problem, for somewhat different reasons.

That isn't necessary a property of the type. It could be a property of the functions that make up each layer . So a variant of stack could set an index for each parallel one (just with # i), and another function could take a function and return a function that takes a pattern and an extra argument that specifies at which layer it operates.

sequence in time?

both Sequence (new) and Signal (new name for old Pattern) are pattern-like (they represent collection of evenst, each with a time-span and value, and you can apply stack and cat etc.)

Yes, Sequence (new) is a misnomer, since it contains Stack (for parallel composition).

My intuition (just from reading the code) is that Signal (current: Pattern) is "bag of events" (flat like a MIDI stream) and the new Sequence is a tree.

@yaxu could you perhaps comment/confirm, to cut the guesswork short ...

variant of stack could set an index for each parallel one (just with # i),

strictly speaking, no, it could not, at least not at type Pattern a - the index must show up somewhere in the type, so it would be Pattern (Int, a), or Pattern ValueMap - as you can put anything in these (well not anything, but all primitive values) ... the polymorphic type is more expressive.

But yes, annotating events with indices is an interesting idea. I should try this some day.

Yes, I meant the patterns that you usually pass to stack.

i wrote a function that bunches up parallel events and puts them into a list, retaining the structure:

https://hackage.haskell.org/package/tidal-1.9.3/docs/Sound-Tidal-Pattern.html#v:collect

with this conversion it's fairly easy to apply a function just to the first elements in a list etc., it's a bit of a hack maybe but it does work

ah good, thank you. I suppose there is no need for this particular task to collect the events, it may be enough to assign a parameter to them? Like

stack [p1 # stackIndex 0, p2 # stackIndex 1, p3 # stackIndex 2]

just fiddling with existing values for stackIndex in p1etc. will have to be dealt with systematically.