Ask Your Question
0

What's the difference between `|+|` and `(+) <$> a <*> b`

asked 2017-01-07 09:36:53 -0500

this post is marked as community wiki

This post is a wiki. Anyone with karma >1 is welcome to improve it.

Why is it sometimes necessary to use completely different syntax to combine patterns?

edit retag flag offensive close merge delete

1 answer

Sort by ยป oldest newest most voted
0

answered 2017-01-07 10:19:06 -0500

this post is marked as community wiki

This post is a wiki. Anyone with karma >1 is welcome to improve it.

The core issue is the difference between basic Patterns and what Tidal calls "ParamPatterns".

"Basic" Patterns

"Pattern" is a generic class of types - you can have Pattern Int: "0 1 2", a Pattern Double: "0.1 0.8", a Pattern String: "rave arpy" or any other type. However, note that these Patterns don't have a name in front like n or sound or speed - they're "basic" types, and don't yet mean anything to a synthesizer (like SuperDirt).

Since these are patterns of types such as "Int" or "Double", you can combine them with ordinary math, as long as you tell Haskell to "lift" or "map" the operators so they work on Patterns of numbers instead of just normal numbers. So that's where you see expressions like

(+) <$> "0 1 2" <*> "4"

or

liftA2 (+) "0 1 2" "4"

(which are equivalent). The <$> <*> and liftA2 stuff isn't special to Tidal; it's how you work with certain kinds of "types of types" in Haskell (specifically Applicative Functors, but you don't have to know that).

ParamPatterns

If you want to actually make music, though, you've got to tell the computer what to do with these numbers, which is where all the parameter keywords like s n speed shape cutoff etc. come in. Deep down, these basically pair the numbers (or strings in some cases) in the "basic" pattern with a parameter to form a ParamPattern, so when messages are sent to SuperDirt it receives a bundles of things like {speed: 0.5}.

For ParamPatterns there are some special operators, because "adding" speed "1 1 2" and speed "3" is very different from "adding" speed "1 1 2" and crush "3". The latter is combining completely different parameters!

So that's where the operators |=| (aka #), |+|, |*|, and |-| come in. These work on ParamPatterns, and will do the math operation only when combining two patterns of the same parameter. So you can type

speed "1 1 2" |+| speed "3"

or even

speed "1 1 2" |+| crush "3"

which will just send both parameters ("merge") without adding the numbers.

But you cannot write (+) <$> (speed "1 1 2") <*> (speed "3"), because Haskell only knows how to use + on numbers, not on parameters.

Which should I use?

Often you can do things either way, so whichever is most convenient. Some Tidal functions (like striate) only work on ParamPatterns, so they can force you to use ParamPattern operators. But these two are completely equivalent:

n ((+) <$> (run 8) <*> "2")

n (run 8) |+| n "2"

If you want to more complex math than just arithmetic, it'll be much easier to work with the number pattern before turning it into a ParamPattern

speed (fmap ((**2) . cos . (/8)) run 32)

which makes the number pattern run 32, divides by 8, takes the cosine, and squares it.

edit flag offensive delete link more

Comments

Is there a reference for all the tidal operators somewhere? I couldn't find it in the documentation.

kisielk gravatar imagekisielk ( 2017-01-09 17:38:33 -0500 )edit

I don't think so, but that sounds like a good suggestion!

bgold gravatar imagebgold ( 2017-01-09 20:49:42 -0500 )edit

Definitely on the list of things to do. Thanks for the suggestion.

sfradkin gravatar imagesfradkin ( 2017-01-10 09:37:44 -0500 )edit

Question Tools

1 follower

Stats

Asked: 2017-01-07 09:36:53 -0500

Seen: 133 times

Last updated: Jan 07