Pipes and composition
A relanote piece is a value, and most transformations on it are functions. The pipe |> is how you chain those functions so the code reads in the order the music flows.
|> is "then"
scale Major = { R, M2, M3, P4, P5, M6, M7 }
let melody = | <1> <3> <5> <3> |
; Without pipes — read inside out.
reverse (transpose P5 melody)
; With pipes — read left to right.
melody |> transpose P5 |> reverseChains
For multi-step transformations, put each step on its own line:
let result = melody
|> transpose P5 ; up a fifth
|> repeat 2 ; then twice
|> reverb 0.3 ; with a little reverbComposing functions without applying them
>> glues functions; the result is a new function:
let style = transpose P5 >> reverse >> repeat 2
melody1 |> style
melody2 |> styleSame shape as a Unix pipeline, only the values flowing through are musical phrases instead of bytes.
Partial application
Most builtins take their non-input arguments first, so currying gives you point-free names for free:
let up_fifth = transpose P5
let doubled = repeat 2
melody |> up_fifth |> doubledLambdas
\arg -> body defines an inline function. Used most often with map, filter, fold:
scale Major = { R, M2, M3, P4, P5, M6, M7 }
let melody = | <1> <2> <3> |
melody |> map (\n -> n + P8) ; everything up an octavePatterns
A handful of pipeline shapes show up over and over.
Transform-then-combine — develop a theme by piping a variation off it, then concatenate:
let theme = | <1> <3> <5> <3> |
let variation = theme |> transpose P4 |> reverse
theme ++ variationBranching on a flag — pipes inside an if:
let mixed = if energetic then
melody |> volume 1.0
else
melody |> volume 0.4A few rules of thumb
- Read top-to-bottom, left-to-right. If a chain reads in any other
- Name intermediates when they have a meaning. A
let variation = - Keep transformations pure. Pipes are at their best when each step
- Compose for reuse. When the same chain appears twice, give it a
direction it's too long; break it up with let.
documents intent better than a long unbroken pipe.
is a function from value to value.
name with >>.
scale Major = { R, M2, M3, P4, P5, M6, M7 }
; Named intermediates, single direction:
let melody = | <1> <3> <5> |
let transposed = melody |> transpose P5
let repeated = transposed |> repeat 2Listen-through example
Same phrase, read left to right: repeat it, reverse it, then accelerate the repeated form.
scale Major = { R, M2, M3, P4, P5, M6, M7 }
let melody = | <1> <2> <3> <5> |:2
let doubled = melody |> repeat(2)
let reversed = melody |> reverse
let fast = doubled |> double_time
melody ++ reversed ++ fast