Combining functions

I have to confess, that I still doesn't really comprehend the principles of combining functions. So please excuse this probably silly question:

Using the ur function, is it possible not only to arrange groups of single patterns but stacks of patterns too?

Ok. Should read first before asking... :wink:

1 Like

I was just about to open a question under the title "Combining functions" when I saw this one. My question is not about ur but I can aptly quote the first few lines: While working with my ear and finding sounds and beats I also really would like to think about and understand the concepts behind Tidal. As a heuristic I am trying to follow the guidelines Carsten Heisterkamp proposed to understand the login behind Tidal expressions and its combinations.

So I took the following code (somewhere from the documentation) and tried to analyze this example in term of signatures, parameters and return values:

d1
  $ off 0.25 (# vowel "<a o i>")
  $ juxBy 0.4 rev
  $ every 2 (rot "<1 3 2>")
  $ n "0 <0 4> [2 0] [2 3]"
  # sound "feel"
  # speed "1.75 2"

Working backwards through the code I got stuck at every and simplified this a bit

d1 $ every 2 (rot "<1 3 2>") $ n "0 ~ 1 2 0 2 ~ 3*2" # sound "drum"

:t every
-- Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a

:t rot
-- Pattern Int -> Pattern a -> Pattern a

No here - finally - is my question: every takes 3 parameters, which, as I understand, are in this case

  • 2 (Pattern Int)
  • (rot "1") (a function taking and returning a pattern (Pattern a -> Pattern a)) and
  • the pattern (Pattern a) to apply rot to, which is produce by everything follwing the $; I am assuming here that Tidal has already calculated the combination of n ... # sound ... and hands over a ControlPattern to every

Is that correct?

Now rot will be applied every 2nd cycle and takes the following parameters:

  • the Pattern Int "<1 3 2>" which provide the shift values (applied in the 2nd, 4th and 6th cycle and so on)
  • and ... now I am confused ... there must be another parameter Pattern a which can only be again what follows after the $

I'd be grateful for hints to clear up my confusion or to correct my analysis if it has gone astray.

1 Like

it sometimes helps to look at (instead of: listen to) events. If you just evaluate the pattern, you see the events of its first cycle:

"a b c":: Pattern String
(0>β…“)|"a"
(β…“>β…”)|"b"
(β…”>1)|"c"

For some patterns, like the one you asked about, you want to see several cycles, so you can do this

queryArc ( every 2 (rot "<1 3 2>") ("a b c":: Pattern String) ) $ Arc 0 8

[[((1,1),(2,1)),((2,1),(4,1))](0>β…“)|"b",[((3,1),(4,1)),((2,1),(4,1))](β…“>β…”)|"c",...

but then the output needs some re-formatting, e.g.,

mapM_ (\e -> print(part e, value e)) $ flip queryArc (Arc 0 8) $ every 2 (rot "<1 3 2>") ("a b c":: Pattern String)
(0>β…“,"b")
(β…“>β…”,"c")
(β…”>1,"a")
(1>1β…“,"a")
(1β…“>1β…”,"b")
(1β…”>2,"c")
...

You can avoid this extra output processing by compressing the pattern into one cycle:

fast 8 $ every 2 (rot "<1 3 2>") ("a b c":: Pattern String)
     (0>1/24)|"b"
  (1/24>1/12)|"c"
     (1/12>β…›)|"a"
        (β…›>β…™)|"a"
     (β…™>5/24)|"b"
     (5/24>ΒΌ)|"c"
...

but then you have to un-compress the timing information (in your head, while reading the output)

NB - Ultimately, look at the code, e.g., every is defined at https://github.com/tidalcycles/Tidal/blob/main/src/Sound/Tidal/Core.hs#L404

That gives you the truth, but that doesn't necessarily make it easier, because the implementation may use functions that are not very much visible on the surface, in this case, innerJoin https://github.com/tidalcycles/Tidal/blob/main/src/Sound/Tidal/Pattern.hs#L463

2 Likes

Yes your analysis of this is all good. I think what ties it together is 'partial application'.

If you have a function plus :: Int -> Int -> Int that adds two numbers together, you could make a function plusthree a = plus 3 a that added three to the given number. Thanks to Haskel's partial application though you can just do plusthree = plus 3. There's a clue to this in the type signature plus :: Int -> Int -> Int. In a way functions in haskell only ever take one input, in this case Int, and the return value is another function of type Int -> Int. So plus 3 4 calles plus with value 3, which returns a function which 4 is passed to, which finally adds them together and hopefully returns 7.

So rot "<1 3 2>" is a function that takes a pattern as input, and returns a rotated pattern as output. That's what gets passed to every 2, which selectively applies it to the pattern in its third parameter.

Without this neat feature, tidal would be a lot more verbose. It's one of those things which is often easier to understand if you haven't used programming languages without it.. It's probably easy to understand that (+ 3) means plus three, if you aren't used to functions getting all their inputs at once.

1 Like

The keyword 'partial application' helps a lot.

I am reading and watching various things (for its clarity and vividness I really like How To Read Function Type Signatures by Christopher Okhravi) around Haskell and functional programming and 'partial application' of course came up already - but I just didn't make the link to this case.

So thanks a lot for that! I will need some time to process but am sure to make some more baby steps now!

1 Like