Iterative functions?

Apologies because:

  • I’m fairly new to Tidal/Haskell/functional programming
  • I’m not sure if this is the right place to post

But I’m interested in working with iterative functions (things like cellular automata, markov chains and so on). I can do this easily enough in Haskell with a recursive function, but I’m not sure how to best make this playable in Tidal. I can write a simple function that, for example, takes a gain pattern as input and returns a different pattern - but is there a way to get that outputted pattern to act as the input for the next repetition (and so on ad infinitum)? I’m struggling here because intuitively I keep wanting to use something mutable to keep state.

I’m sure that there must be multiple ways of doing this - it would be really helpful to hear a few ideas.
Thanks

3 Likes

I have a similar question and ended up figuring something out. The catch is the ‘ad infinitum’ part. If you define an iteration depth you can compute the whole pattern (which may be composed of iterative modifications to the base pattern) all at once and output that.

For example, here is the source code for soak which repeatedly applys some transformation and glues the result to the previous result.

-- d1 $ soak 4 (iter 4) $ s "{bd(3,8), sn(2,5,3)}"
soak :: Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
soak depth f pattern = cat $ take depth $ iterate f pattern

iterate applies some function over and over and returns an ‘infinite’ list of the results. (ie. for some function f and some data x : [x, f(x), f(f(x)), f(f(f(x))), .... ]) take then truncates this list to avoid returning an infinite pattern. Finally, cat glues the resulting patterns together.

Here is a similar idea that attempts to combine the last result with the next result

-- d1 $ note (scale "minor" $ snowball 4 (+) (slow 2 . rev) "0 3 . 2 4 6") # s "gtr"
snowball :: (Pattern a -> Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Int -> Pattern a -> Pattern a
snowball depth combinationFunction f pattern = cat $ take depth $ scanl combinationFunction pattern $ iterate f pattern

I would love to be able to have the transform operate on the output of last cycle forever and ever, like you were describing. That I really don’t know how to do functionally.

2 Likes

Thanks so much - that is really helpful code to look at.

This is the kind of approach I’ve been using these last few days (with some success), but I didn’t know about the iterate function. Actually, I’m feeling a little stupid because I encountered a namespace conflict with iterate right as I was finishing off writing what was essentially my own version of it. But I didn’t look into it at the time. I was iterating through[Int] rows which I transformed into String then made patterns via parseBP_E (which I also haven’t looked deeply into yet), then queued them up together with slowcat.

I can basically pre-calculate an arbitrarily big number of iterations of patterns and then play them through sequentially. The effect is right, but if feels somehow at odds with making music generatively.

I wonder if there is some way to do it in real time, so to speak. It would be great to be able to make music that way in Tidal.

I think there must be some way to do it. Haskell is a general purpose language and I’m pretty sure I’ve even seen some people doing markov chains in tidal before.

There is a concept that I’ve seen andrew sorensen mention called “tempo recursion” where state is passed from iteration to iteration with a delay. I don’t know if its possible to get state from one cycle to another but that could function as a kind of tempo recursion.

EDIT:

Here is what I was thinking of for the markov chains: http://blog.tidalcycles.org/developing-a-markov-chain/

1 Like

Also check out the perlin function, which is like rand but produces related random values.

2 Likes

oh interesting! that neat how it works inside. doing things functionally still makes my brain hurt. That gives me some more ideas on how to make the next cycle based on the last cycle.