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.
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 accidentals k = Map.fromList [ ((o, c), a) | o <- [0..maxOctave] , (c, a) <- zip diatonicSteps $ fifths k , a /= 0 ] where maxOctave = 9 diatonicSteps = [Pitch.C, Pitch.D, Pitch.E, Pitch.F, Pitch.G, 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.