I’ve had quite some fun recently (re)learning Haskell. My learning project is to implement braille music notation parsing in Haskell. Given that i’ve already implemented most of this stuff in C++, it gives me a great opportunity to rethink my algorithms.
Not everything I’ve had to implement until now was actually pretty. I spent yesterday evening implementing accidentals handling, which turned out to be quite a mess. However, I wanted to share my definition of the circle of fifths, because I find it rather concise.
The problem
Given a key signature (often expressed as the number of sharp or flat accidentals), tell which pitch classes are actually raised/lowered.
While reading through music notation software, I have seen several implementations of this basic concept. However, I have never seen one which was so concise.
module Accidental where
import Data.Map (Map)
import qualified Data.Map as Map (fromList)
import qualified Haskore.Basic.Pitch as Pitch
fifths n| n > 0 = let [a,b,c,d,e,f,g] = fifths (n-1) in [d,e,f,g+1,a,b,c]
| n < 0 = let [a,b,c,d,e,f,g] = fifths (n+1) in [e,f,g,a,b,c,d-1]
| otherwise = replicate 7 0
Given this, we can easily define a Map of pitches to currently active accidentals/alterations. List comprehension to the rescue!
accidentals :: Int -> Map Pitch.T Pitch.Relative
= Map.fromList [ ((o, c), a)
accidentals k | o <- [0..maxOctave]
<- zip diatonicSteps $ fifths k
, (c, a) /= 0
, a where
] = 9
maxOctave = [Pitch.C, Pitch.D, Pitch.E, Pitch.F, Pitch.G,
diatonicSteps Pitch.A, Pitch.B]
The full source code for the haskore-braille (WIP) package can be found on GitHub.
If you have any comments regarding the implementation, please drop me a mail.