 # How to apply functions to number Patterns?

Greetings,

What kinds of ways are there to construct or apply number patterns from functions?

I was trying to import some custom arpeggiation code into Tidal. A minor victory was getting an old function to compile/work in Haskell:

``````wrap :: Int -> [Int] -> Int -> Int
wrap x seq jump
| s < 0 = seq!!(s+len) + (j-1)*jump
| otherwise = seq!!s + j*jump
where len = length seq
j = x `div` len
s = x `mod` len
``````

By reading `Pattern.hs`, I found that `withValue` could be used to apply a number function with a single argument to a number pattern.

``````withValue (\a -> wrap a [0,2,4] 7) "0 [2 4] [3 3 3]"
``````

giving the expected result:

``````t> (0>⅓)|0
(⅓>½)|4
(½>⅔)|9
(⅔>⁷₉)|7
(⁷₉>⁸₉)|7
(⁸₉>1)|7
``````

Now, `withValue` just works for a function with a single argument and a single pattern input.

What if I had multiple inputs to the function that I wanted to feed with number patterns? (My goal function has the type signature: `Int -> Int -> [Int] -> Float -> Int -> Int`... thus I would like to map at least two input Int patterns and possibly an array pattern.) What kind of strategies are there for constructing number patterns from complicated functions?

best regards

So, I think I am nearly there. In `Params.hs` I found `pI` and the like used to generate control patterns:

``````cp = pI "a" "0 2 3 4" # pI "b" "0 3 5"
``````

resulting in

``````t> (0>¼)|a: 0, b: 0
(¼>⅓)-½|a: 2, b: 0
¼-(⅓>½)|a: 2, b: 3
(½>⅔)-¾|a: 3, b: 3
½-(⅔>¾)|a: 3, b: 5
(¾>1)|a: 4, b: 5
``````

Then, as an exercise, I tried adding them together directly, which did not work due to the polymorphic type of Value, but then found the `fNum2` helper, which did:

``````--addNumbers vm = (vm Map.! "a") + (vm Map.! "b")
addNumbers vm = fNum2 (+) (+) (vm Map.! "a") (vm Map.! "b")
``````

giving the expected result:

``````t> (0>¼)|0
(¼>⅓)-½|2
¼-(⅓>½)|5
(½>⅔)-¾|6
½-(⅔>¾)|8
(¾>1)|9
``````

It seems like my final adapter function needs an additional layer for pattern matching the possible types of `Value` inputs that come out of the `ValueMap` events from the `ControlPattern`.

I finally got it to work. My adapter function which gets passed to `withValue` looks like this:

``````carpVM :: [Int] -> ValueMap -> Int
carpVM seq vm = carp a b seq drag jump
where mA = Map.lookup "a" vm
mB = Map.lookup "b" vm
mDrag = Map.lookup "drag" vm
mJump = Map.lookup "jump" vm
a = case mA of
Just (VI v) -> v
Nothing -> 0
b = case mB of
Just (VI v) -> v
Nothing -> 0
drag = case mDrag of
Just (VF v) -> v
Nothing -> 0
jump = case mJump of
Just (VI v) -> v
Nothing -> 7
``````

`Pattern` is instance `Applicative` so for a function with the signature

``````f :: Int -> Int -> Int
``````

you can apply it to patterns like

``````f <\$> "0 1 2 3" <*> "0 9 1"
``````

If you want to use structure from only one side (like `*|`, `|+` and the like), you can use `*>` and `<*` to use the structure only from right or left respectively. Incidentally these standard binary operators we all use every day in Tidal are defined exactly like that, e.g.,

``````a |* b = (*) <\$> a <* b
``````

Note: if you want to use `*>` and `<*`, you need to run `import Prelude hiding ((<*), (*>))` to avoid name clashes with the `Prelude` module.

You can extend this to any number of flat arguments by chaining `<*>`, i.e., for `g` of type `Int -> ... -> Int -> Int` you write

``````g <\$> a1 <*> ... <*> an
``````

Now lifting functions with more structured signature, like your `wrap` function, requires a bit more of a mental stretch and will probably involve something like `map`, `foldl`, `foldr` and the like to get the types match up and may depend on the specific application. I'll leave the implementation details up to you for now but let me know if you are stuck and we can work it out together.

You can also do something like

``````(flip wrap [0, 2, 4]) <\$> "0 [2 4] [3 3 3]" <*> "7 1"
``````

which would allow you to use patterns for `x` and `jump` but not for `seq`.

Finally, it might be easier to rewrite the `wrap` function to work on `Patterns` directly rather than lifting if the only purpose is to use it in Tidal.

Good luck and happy experimenting!

2 Likes

Thank you for your illuminating suggestions, Frederik!

you can apply it to patterns like `f <\$> "0 1 2 3" <*> "0 9 1"`

Indeed, I was able to call a modified variant of my original function like this:

``````( (carp2 [0,2,4]) <\$> "[0 1 2 3 4]/3" <*> 0 <*> 1 <*> 7)
``````

One downside is that this form forces us to use a lot of default parameters... that is, I would love to omit them (the scalars at the end) when I am not using them.

About the `seq` parameter, I have some fundamental doubts that you allude to:

Now lifting functions with more structured signature, like your `wrap` function, requires a bit more of a mental stretch and will probably involve something like `map`, `foldl`, `foldr` and the like to get the types match up and may depend on the specific application.

Is it possible to use the mini-notation superposition and somehow get Lists, arrays of values out of that? For example, with a pattern like `"0 [2,4]"`, the superposed values are separated into coincident events:

``````t> (0>½)|0
(½>1)|2
(½>1)|4
``````

If we used `fmap` or `<\$>` it would probably process the events separately. We would probably want to find some way to collect the superposed events into a pattern of Lists. Maybe possible with one of the `filter` functions from `Patterns.hs`, perhaps?

If we used `fmap` or `<\$>` it would probably process the events separately. We would probably want to find some way to collect the superposed events into a pattern of Lists. Maybe possible with one of the `filter` functions from `Patterns.hs`, perhaps?

As far as I know mini notation does not support lists but I'm not too familiar with it so I might be wrong. Your observation is correct that it would process them independently. To make things worse, you would get type errors, because you have scalars not lists. So as you suggested it would be nice to have a function `listify :: Pattern a -> Pattern [a]` that lumps simultaneous events together. It will not be easy if you are not familiar with Haskell and how patterns are implemented in Tidal but could be a good exercise in understanding it. (But I would really say it's an advanced exercise.)

The easier approach would be to not use mini notation but the functions `fastcat`, `slowcat`, `fast`, `slow`, etc. directly, e.g.,

``````fastcat [return , return [2,4]]
``````

will create the example pattern. You can use the table from the documentation on mini-notation as a reference. If you find writing `return` for each element in the list tedious, you can map `return` over the whole list like so:

``````fastcat \$ return <\$> [, [2,4]]
``````

or even define a shortcut like `rt = (return <\$>)` if you're going to use a lot of nested structures which would shorten it to

``````fastcat \$ rt [, [2,4]
``````

Still not as concise as mini-notation but maybe enough for experimenting...

1 Like

Not having default parameters is fundamentally integrated into Haskell. There are some ways to have defaults using record types but it will not make things easier in your case. What people usually do in Haskell is define a general function and then a special case function with the default values set, e.g.,

``````degrade = degradeBy 0.5
``````

Here `degrade` is defined as the special case of the `degradeBy` function, where the dropout probability is 0.5.

1 Like

I am definitely curious to go down this road, just a little bit. At the moment I am hard pressed to find where the `Event` type is defined (in order to understand `Pattern`).

I’m away from a computer at the moment but running `:i Event` in a tidal repl should tell you in which module it’s defined

Thanks, Paul. That was helpful for finding the definition!

1 Like

I see you keep rejecting my easy solutions so no more light treatment for you then... How is your progress with the `listify` function? Do you need pointers? I got intrigued by the problem as well and implemented it myself using `foldr` and a few helper functions. If you need a clue how to get started you can reveal the spoiler below

Spoiler
``````listify :: Pattern a -> Pattern [a]
listify p = Pattern \$ collect . query p
where
collect :: [Event a] -> [Event [a]]
collect events = foldr add [] events
add :: Event a -> [Event [a]] -> [Event [a]]
``````

The `add` function should check if the event `e` has an event in `es` that occurs at the same time and then add its value to the event. Otherwise it should add the event `e` to the list `es` (with the structure of its value adapted to the require type). You might want to use recursion. Let me know if you need more hints.

If you're going to get serious about writing code in Haskell, I strongly recommend installing the Haskell Language Server (hls) which makes "getting stuff to compile" and finding the definitions much easier. If you're using neovim I can help you set it up but for many other ide you will also find good documentation online.

How is your progress with the `listify` function?

I'm still working on it, after pausing to read a bit about Haskell types. Hopefully, I can try some experiments before looking at your solution.

Your construction of list patterns blew my mind:

``````fastcat [return , return [2,4]]
``````

i.e. I had to verify that `return ` was actually a `Pattern [Int]`. Just now I was able to conclude that this works due to the `Pattern` implementation of `Monad` and `Applicative`, where `pure` and `return`, synonyms, construct a kind of constant pattern.

So, I have the first version based on your helpful template! I haven't checked all aspects for correctness, but it runs on a simple example. If it is not incorrect, then there are probably many ways to improve the style.

``````import Data.Maybe (maybe, isJust, fromJust)
import Data.List (find, findIndex)

listify :: Pattern a -> Pattern [a]
listify p = Pattern \$ collect . query p
where
collect :: [Event a] -> [Event [a]]
collect events = foldr add [] events

--check if the event e has an event in es that occurs at the same time
--and then add its value to the event.
--Otherwise it should add the event e to the list es
add :: Event a -> [Event [a]] -> [Event [a]]
| isJust matchedInd = replaceEventI es (fromJust matchedInd) augmentedE
| otherwise = (convertEvent e):es
where matchedE = find (eventsCoincident e) es
matchedInd = findIndex (eventsCoincident e) es
augmentedE = maybe (convertEvent e) (augmentEvent (value e)) matchedE

--give an event augmented with a value
augmentEvent :: a -> Event [a] -> Event [a]
augmentEvent val ev = Event { context=context ev,
whole=whole ev,
part=part ev,
value=val:(value ev) }

--convert to a list event
convertEvent :: Event a -> Event [a]
convertEvent ev = Event { context=context ev,
whole=whole ev,
part=part ev,
value=[value ev]}

--do two events  reflect the same time?
eventsCoincident :: Event a -> Event [a] -> Bool
eventsCoincident x y =
(part x == part y) && (whole x == whole y)

--replace the Event at index I
replaceEventI :: [Event [a]] -> Int -> Event[a] -> [Event [a]]
replaceEventI es i b = (fst split) ++ (b:(tail (snd split)))
where split = splitAt i es
``````
1 Like

Good job!

#### update record types

There is a simplified syntax for updating record types in which case you only need to specify the new values which would allow you to write something like

``````convertEvent ev = ev { value = [value ev] }
``````

and similarly for `augmentEvent`.

#### pattern matching

Instead of `isJust` and `fromJust` you can just pattern match against the different cases:

``````add e es = case matchedInd of
Just match -> replaceEventI es match augmentedE
Nothing    -> (convertEvent e):es
where ...
``````

which admittedly is not that much shorter but it avoides the partial (=evil) function `fromJust`.

#### multiple iterations

Now one thing that strikes me is that in the code as it is, you iterate over the same list of events multiple times, 1st to find the matching event, 2nd to find the matching events index and finally to split at that index and insert the new value. If you write the iteration yourself as a recursion, you can do all these things at once, i.e.,

``````add e [] = convertEvent e  -- base case (we have reached the end of the list)
add e e':es | eventsCoincident e e' = (augmentEvent (value e) e') : es  -- it's a match -> add value
add e e':es | otherwise             = e' : (add e es)                   -- no match -> continue recursion
``````

#### conclusion

I see you make good use of the common types and functions in Haskell. If you haven't heard of it yet, I recomend you have a look at Hoogle which allows searching Haskell libraries by name and even by type thus providing you with the right tool for the right job many times (it even searches the tidal library). However, sometimes a more hands-on approach can produce more concise code but requires more familiarity with the functional paradigms like recursion and folds, just like a for-loop sometimes is the best approach in an imperative language. Spotting this is of cause a matter of practice so I'm confident this will get more and more easy for you.

Neat code doesn't really write itself. Sometimes when problems are a bit more complicated, I write pseudo code as a for loop and then translate the whole thing into functional style (sometimes when writing Python I go the other way around as well). This takes several steps from a rather bulky direct translation through several steps of finding patterns that allow for simplification until you end up with just a few lines of code -- just the way this discussion is going right now.