1.7 Abstraction³
The abstraction principle is the idea of separating patterns from the instances in which they appear.
1.7.1 Naming
An example of abstraction and naming might be something like:
pi :: Double
pi = 3.141592653589793
This allows changing the value in one place vs the many places it might appear i a program. In computer music a pitch consists of a pitch class and an octave. In Euterpea “concert A” is represented as
A,4) (
in music this represents the picth class A in the fourth octave. Defining this looks like:
concertA :: (PitchClass, Octave)
a440,= (A,4) -- concert A
a440 = (A,4) -- A440
concertA {-
As a note multi line comments in haskell look
like this.
-}
We can give several names to the same value in haskell which may help think about different representations of the data as in Machine Learning
A larger expression may require writing things more than once:
x :: Float
= f (pi * r ** 2 ) + g (pi * r ** 2 ) x
We can abstract this in Haskell by using another expression
area :: Float
area :: pi * r ** 2
x :: Float
= f area + g area x
We can also limit areas scope using a let expression.
x :: Float
= let area = pi * r ** 2
x in f area + g area
The name for expressions like the one above for 𝜋
is called a binding.
1.7.2 Functional Abstraction
If the original problem looked like:
x :: Float
= f (pi * r1 ** 2 ) + g (pi * r2 ** 2 ) x
we could stll abstract this to
x :: Float
= let areaF r = pi * r ** 2
x in f (areaF r1) + g (areaF r2)
This is called functional abstraction.
Now a bit of music theory. > A note in Euterpea is defined as pitch combined with a duration.
A duration is measured in beats.
In Euterpea this has type
Dur
One note with duration of 1 beat is called a whole note
One note with duration of ½ beat is called a half note
This is the smallest performable peace of music in Euterpe besides a rest
Its type is MusicPitch
There functions typesignatures are as follows.
note :: Dur -> Pitch -> MusicPitch = ??? note d p rest :: Dur -> MusicPitch = ??? rest d {-Example-} 1/4) (A,4) -- quarter note concert A note (1 -- rest one beat rest
In Euterpea infix operators combine smalle music values into larger ones…
(:+:) :: MusicPitch -> MusicPitch -> MusicPitch (:=:) :: MusicPitch -> MusicPitch -> MusicPitch :+: m2 -- plays music pitch m1 and then m2 m1 :=: m2 -- plays music pitch m1 and m2 simultaneously m1
Euterpea also has a function
trans :: Int -> Pitch -> Pitch -- this is the pitch that is i semitones higher than p trans i p
1.7.3 Data Abstraction
Representing situations where the number of values is uncertain as a data structure is useful. To add a single element to the front of a list in haskell the following is used:
: xs
x C : [D, Ef] == [C, D, Ef] == C : D : Ef : []
{- these are all valid ways of creating lists -}
for a harmonized list we can define a function hList
hList :: Dur -> [Pitch] -> MusicPitch -- type signature
= rest 0 -- MusicPitch with 0 duration
hList [] : ps) = let hNote dp = note d p :=: note d (trans (-3) p)
hList d (p in hNote d p + hList d ps
Exercise 1.4
mel :: Integer -> Dur -> [Pitch] -> MusicPitch
= let hList :: Integer -> Dur -> [Pitch] -> MusicPitch
mel = rest 0 -- MusicPitch with 0 duration
hList i d [] : ps) =
hList i d (plet hNote :: Integer -> Dur -> Pitch -> MusicPitch
= i d p = note d p :=: note d (trans i p)
hNote in hNote i d p :+: hList i d ps
in hList i d l
1.8 Haskell Equality vs. Musical Equality
Euterpea formally defines musical interpretation so that the difference of adding a MusicPitch
with duration 0 is not different from A MusicPitch
without.. this leads to the axiom:
m :+: rest 0 ≡ m
1.9 Code Reuse and Modularity
Replacing code with smaller abstractions allows us to reuse code and simplify implementation even though this may require more code it is still clearer than the single use of an expression.