Dear Lounge, I was reading the source
instance Ord a => Ord (Pattern a) where
min = liftA2 min
max = liftA2 max
compare = noOv "compare"
(<=) = noOv "(<=)"
and I thought - why allow min and max, but not compare? I think I have an explanation, and an application for that. (Well, you decide whether it's useful at that.)
Explanation:
it's the types (of course it is). We have min :: Ord a => a -> a -> a, and when we instantiate a with Pattern a, we get min @(Pattern _) :: Ord w => Pattern w -> Pattern w -> Pattern w. How does it work? Same way we multiply or add: combine each value from the left with each value from the right (for overlapping events).
min (3 - run 3) (run 4)
(0>ΒΌ)|0
(ΒΌ>β
)|1
(β
>Β½)|1
(Β½>β
)|2
(β
>ΒΎ)|1
(ΒΎ>1)|1
Why does this not work for compare? The type is compare :: Ord a => a -> a -> Ordering, after instantiation: compare @(Pattern _) :: Ord w => Pattern w -> Pattern w -> Ordering (and not: ... -> Pattern Ordering), and that has no sensible semantics (how would you define one pattern to be smaller than another).
Application:
sorting a list of patterns (if their elements are orderable, e.g., numbers (for notes)). The catch is that we cannot use Data.List.sort (mergesort): it's statically correct (the types match) but dynamic semantics (value) is "throws an exception".
tidal> import Data.List (sort)
tidal> sort @(Pattern Int) [pure 1, pure 2]
*** Exception: compare: not supported for patterns
because the implementation will call compare, which we did not implement.
And now the nice thing is that we can still sort - using min and max only! We write a data-oblivious program (control flow does not depend on input data). Such sorting algorithms exists, and they are "sorting networks", consisting of compare-and-swap gates: input (x,y) gives output (min x y, max x y). A simple realisation is odd-even transposition sort (Odd-even transposition sort).
In the following example, I make a random sequence (of notes) as input to the network, and we hear each layer in turn (in fact, two times, so we have a chance to adapt). Last layer is sorted (from lowest to highest note). For example, on input [4,3,2,1,0], we get these layers:
[[4,2,3,1,0],[2,4,1,3,0],[2,1,4,0,3],[1,2,0,4,3],[1,0,2,3,4],[0,1,2,3,4]]. (source code https://gitlab.imn.htwk-leipzig.de/waldmann/computer-mu/-/blob/master/tidal/code/sorting.tidal , audio https://gitlab.imn.htwk-leipzig.de/waldmann/computer-mu/-/blob/master/tidal/code/sorting.ogg )
Challenge question: in my code, change [0 .. 5] to [0 .. 7] (or similar), observe the glitches (skips), and explain.
Related work
This was, of course, inspired by Earth-to-Abigail's work on sonification of sorting ( https://www.earthtoabigail.com/blog/audio-representation-bubble-sort-with-ruby-sonicpi ) - and I only found out recently that they seem use this prominently in Passarinho Azul (The Forest | Earth to Abigail ). Well, that's pure beauty - which I avoided by using the chromatic scale, to get a more brutalist effect.