Gibt es eine Möglichkeit, "C-Dur" anstelle von "C-Dur" zu schreiben?

Ich bin in meinem Musikprojekt auf ein kleines ästhetisches Problem gestoßen, das mich schon seit einiger Zeit stört.

Ich habe einen Typ Daten Key = C | D | ... und ich kann eine Scale aus einem Key und einem Mode konstruieren. Der Mode unterscheidet z.B. zwischen einer Dur- und einer Molltonleiter.

Ich kann den Typ Mode als eine Funktion von Key zu Scale definieren. In diesem Fall haben die Modi Namen in Kleinbuchstaben (was in Ordnung ist) und ich kann eine Skala wie diese erhalten

aScale = major C

Aber Musiker sprechen nicht so. Sie bezeichnen diese Skala als _C-Dur-Tonleiter, nicht als C-Dur-Tonleiter.

Was ich will

Idealerweise würde ich schreiben wollen

aScale = C major

Ist das überhaupt möglich?

Was ich versucht habe

Ich kann Key zu einer Funktion machen, die eine Scale aus einem Mode konstruiert, also kann ich schreiben

aScale = c Major

Aber ich kann Keys nicht auf die Konstruktion von Skalen beschränken. Sie werden auch für andere Dinge benötigt (z.B. für die Konstruktion von Akkorden). Auch Key sollte eine Instanz von Show sein.


Ich kann den Mode nach dem Key setzen, wenn ich eine zusätzliche Funktion (oder einen Wertkonstruktor) verwende:

aScale = scale C major mit scale :: Tonart -> Modus -> Skala

Aber das zusätzliche Wort Skala sieht störend aus, und im Gegensatz zu seinem Namen hat Skala nicht wirklich mit Skalen zu tun. Der intelligente Teil ist in Major, Skala ist eigentlich nur flip ($).


Die Verwendung eines newtype Mode = Major | Minor ... ändert nicht wirklich viel, außer dass scale intelligenter sein muss:

aScale = scale C Major
Kommentare zu der Frage (5)

Lösung 1:

Verwenden Sie diese

data Mode  = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode 

Jetzt können Sie schreiben (mit großem C und großem M)

aScale = C Major

Lösung 2a:

Auch dies ist möglich

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B 

data Scale = Scale Key Mode  

Jetzt schreiben Sie

aScale = Scale C Major

Lösung 2b:

Auch dies ist möglich

data Mode  = Major | Minor
data Key   = C | D | E | F | G | A | B 

type Scale = (Key, Mode)  

Jetzt schreiben Sie

aScale = (C, Major)
Kommentare (1)

Hier ist eine skurrile Lösung, die ich nicht wirklich empfehle, die aber sehr "musikalisch" aussieht:

infix 8 ♮
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
 -- ≡ flip ($)

Dann können Sie schreiben

> C♮ major :: Scale

Das Ziel ist natürlich, dass man auch F♯ minor und B♭ major usw. hat.

Kommentare (2)

Wenn Sie nichts gegen einen zusätzlichen Operator haben, könnten Sie & aus Data.Function verwenden. Unter der Annahme, dass major eine Funktion Key -> Scale ist, könnten Sie C & major schreiben. Das ergibt einen Scale-Wert:

Prelude Data.Function> :t C & major
C & major :: Scale
Kommentare (0)

Es gibt bereits mehrere gute Antworten, aber hier ist eine Lösung im Stil des Continuation-Passing-Stils, die hilfreich sein könnte (vielleicht nicht für dieses spezielle Beispiel, aber in anderen Kontexten, in denen eine Art Syntax für die umgekehrte Anwendung gewünscht wird).

Mit Standarddefinitionen für einige Problemdomänentypen:

data Mode = Major | Minor                 deriving (Show)
data Key = C | D | E | F | G | A | B      deriving (Show)
data Semitone = Flat | Natural | Sharp    deriving (Show)

data Note = Note Key Semitone             deriving (Show)
data Scale = Scale Note Mode              deriving (Show)
data Chord = Chord [Note]                 deriving (Show)

können Sie einen Continuation-Passing-Typ einführen:

type Cont a r = (a -> r) -> r

und schreibe die primitiven Notizbau-Typen, um `Content'-Typen wie diese zu bauen:

a, b, c :: Cont Note r
a = mkNote A
b = mkNote B
c = mkNote C
-- etc.
mkNote a f = f $ Note a Natural

flat, natural, sharp :: Note -> Cont Note r
flat    = mkSemi Flat
natural = mkSemi Natural
sharp   = mkSemi Sharp
mkSemi semi (Note k _) f = f $ Note k semi

Dann können die Tonleiter-, Noten- und Akkordbildungsfunktionen die Cont's in einfache Typen in jeder Postfix-Form auflösen (d.h. als Fortsetzungen, die an dieCont's übergeben werden):

major, minor :: Note -> Scale
major n = Scale n Major
minor n = Scale n Minor

note :: Note -> Note
note = id

oder Präfix-Form (d.h. unter Verwendung von Content als Argument):

chord :: [Cont Note [Note]] -> Chord
chord = Chord . foldr step []
  where step f acc = f (:acc)

Jetzt können Sie schreiben:

> c sharp note
Note C Sharp
> c note
Note C Natural
> c major
Scale (Note C Natural) Major
> b flat note
Note B Flat
> c sharp major
Scale (Note C Sharp) Major
> chord [a sharp, c]
Chord [Note A Sharp,Note C Natural]

Beachten Sie, dass c selbst keine Show'-Instanz hat, aberc note` schon.

Mit einer Modifikation des Note-Typs könnten Sie leicht doppelte Vorzeichen unterstützen (z.B. cis sharp, verschieden von d), usw.

Kommentare (1)

Aber ich kann die Schlüssel nicht auf die Konstruktion von Skalen beschränken. Sie werden auch für andere Dinge benötigt (z.B. für die Konstruktion von Akkorden). Auch Key sollte eine Instanz von Show sein.

Sie können Typklassen verwenden, um das geschickt zu umgehen:

{-# LANGUAGE FlexibleInstances #-}

data Key = C | D | E | F | G | A | B deriving(Show)

data Mode = Major | Minor

data Scale = Scale Key Mode

class UsesKey t where
  c, d, e, f, g, a, b :: t

instance UsesKey Key where
  c = C
  d = D
  e = E
  f = F
  g = G
  a = A
  b = B

instance UsesKey (Mode -> Scale) where
  c = Scale C
  d = Scale D
  e = Scale E
  f = Scale F
  g = Scale G
  a = Scale A
  b = Scale B

aScale :: Scale
aScale = c Major

Jetzt können Sie die Kleinbuchstaben auch für andere Typen verwenden, indem Sie entsprechende Instanzen definieren.

Kommentare (0)