C'è qualche possibilità di scrivere "C maggiore" invece di "maggiore C"?


39

Ho riscontrato un piccolo problema estetico nel mio progetto musicale e mi ha infastidito da tempo.

Ho un tipo data Key = C | D | ...e posso costruire a Scaleda a Keye a Mode. La Modedistinzione tra ad esempio una scala maggiore e una minore.

Posso definire il Modetipo come funzione da Keya Scale. In tal caso le modalità avranno nomi in minuscolo (il che va bene) e posso ottenere una scala come questa

aScale = major C

Ma i musicisti non parlano così. Si riferiscono a questa scala come alla scala C maggiore , non alla scala C maggiore .

Quello che voglio

Idealmente vorrei scrivere

aScale = C major

Ciò è effettivamente possibile?

Quello che ho provato

Posso creare Keyuna funzione che costruisce a Scaleda a Mode, così posso scrivere

aScale = c Major

Ma non posso limitare le chiavi alla costruzione di scale. Sono necessari anche per altre cose (ad esempio la costruzione di accordi ). Inoltre Keydovrebbe essere un'istanza di Show.


Posso mettere il Modedopo il Keyquando uso una funzione extra (o costruttore di valore):

aScale = scale C major con scale :: Key -> Mode -> Scale

Ma la scala di parole extra sembra rumorosa e contrariamente al suo nome, scalenon è realmente interessata alle scale. La parte intelligente è dentro major, scaleè davvero giusta flip ($).


L'uso di a newtype Mode = Major | Minor ...non cambia molto, tranne scaleper essere più intelligente:

aScale = scale C Major

3
Mi sono ritrovato a desiderare una sintassi estremamente simile in passato, ma TBH non ne vale la pena. Vai e basta major C.
lasciato circa il

4
Proprio come un cavillo musicale: "Key" è un nome fuorviante per quel tipo di dati, poiché ad esempio C major e C minor sono chiavi diverse nella terminologia standard. "PitchClass" sarebbe un nome più preciso per il tipo.
PLL

2
@PLL In effetti, ho problemi a trovare un buon nome per C, C #, D ... So che Euterpea usa PitchClass. È più corretto di Key, ma non "musicale". In questo momento sto giocando con l'idea di chiamarlo Root o Tonic, ma ciò suggerisce solo accordi e scale. Come diavolo chiamano i musicisti quella cosa: una nota senza ottava?
Martin Drautzburg,

4
@MartinDrautzburg: Non direi che la lezione di pitch non sia musicale - non è solo un modo di parlare da programmatore, è stata stabilita nella teoria musicale come significato "una nota senza un'ottava" almeno dalla metà del 20 ° secolo. Non è molto comunemente al di fuori dei contesti tecnici della teoria musicale, ma è solo perché la precisa distinzione tra "un tono" e "un tono senza un'ottava" non è realmente necessaria spesso nell'uso quotidiano, e quando è necessario, di solito è chiaro dal contesto. Ma "Root" o "Tonic" suonano bene come termini leggermente più familiari, se meno precisi.
PLL

1
No perché non funziona viceversa, un programmatore che va in musica
Emobe

Risposte:


29

Soluzione 1:

Usa questo

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

Ora puoi scrivere (con la maiuscola C e la maiuscola M)

aScale = C Major

Soluzione 2a:

Anche questo è possibile

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

data Scale = Scale Key Mode  

Adesso scrivi

aScale = Scale C Major

Soluzione 2b:

Anche questo è possibile

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

type Scale = (Key, Mode)  

Adesso scrivi

aScale = (C, Major)

L'IMO con la soluzione 2 ti servirà bene. Abbandonati alla sintassi di Haskell e trasformalo in un modello pulito del tuo dominio. Le cose possono essere piacevoli se lo fai
luqui

16

Ecco una soluzione stravagante che non consiglio davvero, ma sembra molto "musicale":

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

Quindi puoi scrivere

> C major :: Scale

Certo, dove questo è davvero mirato è che avresti anche F♯ minored B♭ majorecc.


1
Mi chiedo se ci sia qualcosa di simile allo spazio libero che è consentito come operatore :)
Chepner

26
@chepner attualmente sì: U + 2800 BRAILLE PATTERN BLANK può essere usato come infisso. Inutile dire che questa è un'idea orribile ... Tutti gli effettivi personaggi spaziali sono vietati come infissi, ma non sorprende che Unicode contenga qualcosa che può essere hackerato allo scopo di abuso.
lasciato circa il

11

Se non ti dispiace un operatore in più, potresti usare &da Data.Function. Supponendo che majorsia una funzione Key -> Scale, potresti scrivere C & major. Questo produce un Scalevalore:

Prelude Data.Function> :t C & major
C & major :: Scale

4

Esistono già diverse risposte valide, ma ecco una soluzione di passaggio di stile che può essere utile (forse non per questo esempio particolare, ma in altri contesti in cui si desidera una sorta di sintassi per l'applicazione inversa).

Con definizioni standard per alcuni tipi di dominio problematici:

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)

puoi introdurre un tipo di passaggio di continuazione:

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

e scrivi i tipi primitivi di costruzione delle note per creare Conttipi come questi:

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

Quindi, le funzioni di costruzione di scale, note e accordi possono risolvere Conts in tipi normali in entrambi i formati postfix (ovvero, come continuazioni da passare a Cont):

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

note :: Note -> Note
note = id

o prefisso form (ovvero, prendendo Conts come argomenti):

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

Ora puoi scrivere:

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

Nota che di per csé non ha Showun'istanza, ma sì c note.

Con una modifica al Notetipo, potresti facilmente supportare doppi incidenti (ad esempio c sharp sharp, distinti da d), ecc.


Bello. In realtà ho provato a risolvere il mio problema con Cont, ho cercato di attaccarlo ai costruttori A | B | C ...invece di usare le funzioni. Non riuscivo a farlo funzionare e ancora non capisco perché, dato che i costruttori di valore sono solo funzioni. Se riesco a mettere una funzione davanti ai miei tasti, molte cose diventano possibili. Se la funzione è flip ($)allora ottengo il tuo modello flip ($) B :: Cont Key r. Il mio originale aScale = scale C Majornon è molto diverso.
Martin Drautzburg,

3

Ma non posso limitare le chiavi alla costruzione di scale. Sono necessari anche per altre cose (ad esempio la costruzione di accordi). Anche Key dovrebbe essere un'istanza di Show.

Puoi usare le macchine da scrivere per aggirare abilmente quello:

{-# 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

Ora puoi usare le lettere minuscole anche per altri tipi definendo le istanze appropriate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.