Accidentals in Haskell

Posted on October 14, 2015
Tags: Functional programming, Haskell, Haskore, Music notation

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
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.