Module

Harmonia.Anchor

Package
purescript-harmonia
Repository
afcondon/purescript-harmonia

Harmonia.Anchor — a chord's scale reading and the operations it unlocks.

A chord in Harmonia is one of two things: a DegreeChord + Key (a chord that knows where it sits in a scale — its numeral, quality, and any borrowing) or just a bag of pitches with no reading at all. The Anchor type names that distinction directly:

  • Located key dc — the chord carries its scale context.
  • Free — pitches only; no scale to interpret it against.

The grade of an anchor is not stored — it is derived by reading the recipe against its key (classify). Grade is an ordinal ladder of how much harmonic leverage the chord affords:

ShiftOnly < Keyed < Diatonic

and each rung permits a nested set of operations (permitted). Nothing here forbids a chord from being Free — an unlocated chord is a perfectly legal inhabitant. The type gives leverage (a total classification a caller can pattern-match exhaustively) without making the "illegal" state unrepresentable. Rendering — colour, text, greyed-out buttons — is the caller's job; this module only says what is true, never how to show it.

Pure Prelude/Data.*; builds unchanged on JS and purerl.

#Anchor Source

data Anchor

A chord's scale reading. Located carries the full recipe and its key; Free is a chord that has left home (hand-entered, dragged out of key, or caught with no context) and can only be shifted.

Constructors

Instances

#Grade Source

data Grade

How much harmonic leverage a chord affords, low to high. The order is the whole point: capabilities nest, so Ord is meaningful — a Diatonic chord can do everything a Keyed one can, and more.

Constructors

Instances

#Op Source

data Op

The operations a chord can be asked to undergo. Shift is the floor (always available); the rest need progressively richer readings. A closed set: these are program-logic alternatives, so extending them is a deliberate recompile, never open user data.

Constructors

Instances

#classify Source

classify :: Key -> DegreeChord -> Grade

The v1 judgment. A located chord is Diatonic when it wears no modal- interchange override and every pitch it realizes falls inside the key's scale. Otherwise it is Keyed — it still has a tonic to modulate around, but it is foreign enough that reflavouring it into another mode is ill-defined (there is no single "same degree" to re-read).

This is deliberately conservative and needs no chord-analyser: an out-of-scale chord simply drops to Keyed, so downstream operations pass it through under blame rather than silently mangling it. The place to grow later is upward — recognising a chromatic chord as a secondary function (V/x, viio/x) of a phrase degree and promoting it back to Diatonic in context. That promotion belongs to the phrase, not the lone chord (see Harmonia.Graded).

#gradeAnchor Source

gradeAnchor :: Anchor -> Grade

The grade of an anchor, derived. Free is always ShiftOnly; a Located chord is classified against its key.

#permitted Source

permitted :: Grade -> Array Op

The verbs a grade permits — nested, so higher grades are supersets. This is where the musical knowledge "reflavour ⇒ modulate ⇒ shift" lives, so no caller has to re-derive it.

#permits Source

permits :: Op -> Grade -> Boolean

Is this operation available at this grade?

#scalePCs Source

scalePCs :: Key -> Array Int

The pitch classes of a key's scale.

#chordInScale Source

chordInScale :: Key -> DegreeChord -> Boolean

Does every pitch class of the realized chord lie in the key's scale?

#diatonicQuality Source

diatonicQuality :: Mode -> Numeral -> Boolean -> Quality

The diatonic chord quality on a given degree of a mode, built by stacking thirds within the scale. This is what makes reflavouring real: the same numeral read in a new mode becomes whatever that mode's own harmony makes it — I maj7 in Ionian becomes i m7 in Aeolian, not a transposed Imaj7.

seventh chooses a four-note (true) or three-note (triad) reading, so a reflavour preserves the chord's size while re-deriving its flavour.

#isSeventh Source

isSeventh :: Quality -> Boolean

Does this quality carry a seventh?