Y a-t-il une chance d'écrire «C majeur» au lieu de «majeur C»?

39

J'ai rencontré un petit problème esthétique dans mon projet musical et cela me dérange depuis un certain temps.

J'ai un type data Key = C | D | ...et je peux construire un à Scalepartir d'un Keyet d'un Mode. Le fait la Modedistinction entre, par exemple, une échelle majeure et une échelle mineure.

Je peux définir le Modetype en fonction de Keyà Scale. Dans ce cas, les modes auront des noms en minuscules (ce qui est bien) et je peux obtenir une échelle comme celle-ci

aScale = major C

Mais les musiciens ne parlent pas comme ça. Ils se réfèrent à cette échelle comme l' échelle majeure en C , et non pas l' échelle majeure en C.

Ce que je veux

Idéalement, je voudrais écrire

aScale = C major

Est-ce possible?

Ce que j'ai essayé

Je peux faire Keyune fonction qui construit un à Scalepartir d'un Mode, donc je peux écrire

aScale = c Major

Mais je ne peux pas limiter les clés à la construction des échelles. Ils sont également nécessaires pour d'autres choses (par exemple la construction d' accords ). Devrait également Keyêtre une instance de Show.


Je peux mettre l' Modeafter Keyquand j'utilise une fonction supplémentaire (ou constructeur de valeur):

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

Mais l' échelle de mot supplémentaire semble bruyante et contrairement à son nom, elle scalene s'intéresse pas vraiment aux échelles. La partie intelligente est dedans major, scalec'est vraiment juste flip ($).


Utiliser un newtype Mode = Major | Minor ...ne change pas vraiment grand chose, sauf qu'il scaledoit être plus intelligent:

aScale = scale C Major
Martin Drautzburg
la source
3
Je me suis moi-même retrouvé à vouloir une syntaxe extrêmement similaire dans le passé, mais TBH n'en vaut pas la peine. Allez-y major C.
leftaroundabout
4
Tout comme un petit problème musical: «Key» est un nom trompeur pour ce type de données, car par exemple C majeur et C mineur sont des clés différentes dans la terminologie standard. «PitchClass» serait un nom plus précis pour le type.
PLL
2
@PLL En effet, j'ai du mal à trouver un bon nom pour C, C #, D ... Je sais qu'Euterpea utilise PitchClass. C'est plus correct que Key, mais pas du tout "musical". En ce moment, je joue avec l'idée de l'appeler Root ou Tonic, mais cela ne suggère que des accords et des gammes. Comment les musiciens appellent-ils cette chose - une note sans octave?
Martin Drautzburg
4
@MartinDrautzburg: Je ne dirais pas que la classe de pitch n'est pas musicale - ce n'est pas du tout programmeur, c'est établi dans la théorie musicale comme signifiant "une note sans octave" depuis au moins vers le milieu du 20ème siècle. Ce n'est pas très courant en dehors des contextes techniques de théorie musicale, mais c'est simplement parce que la distinction précise entre «un pitch» et «un pitch sans octave» n'est pas vraiment nécessaire souvent dans un usage quotidien, et quand c'est nécessaire, c'est généralement clair du contexte. Mais «Root» ou «Tonic» sonnent bien comme des termes un peu plus familiers, mais moins précis.
PLL
1
Non, car cela ne fonctionne pas vice versa, un programmeur se
lance

Réponses:

29

Solution 1:

Utilisez ceci

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

Vous pouvez maintenant écrire (avec les majuscules C et les majuscules M)

aScale = C Major

Solution 2a:

C'est aussi possible

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

data Scale = Scale Key Mode  

Maintenant tu écris

aScale = Scale C Major

Solution 2b:

C'est aussi possible

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

type Scale = (Key, Mode)  

Maintenant tu écris

aScale = (C, Major)
Elmex80s
la source
L'OMI avec la solution 2 vous sera très utile. Abandonnez-vous à la syntaxe de haskell et faites-en un modèle propre de votre domaine. Les choses peuvent être agréables si vous le faites
luqui
16

Voici une solution fantaisiste que je ne recommande pas vraiment, mais qui semble très «musicale»:

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

Ensuite, vous pouvez écrire

> C major :: Scale

Bien sûr, où cela vise vraiment, c'est que vous auriez aussi F♯ minoret B♭ majoretc.

à gauche
la source
1
Je me demande s'il y a quelque chose comme un espace insécable qui est autorisé en tant qu'opérateur :)
chepner
26
@chepner en fait oui: U + 2800 BRAILLE PATTERN BLANK peut être utilisé comme infixe. Inutile de dire que c'est une idée horrible ... Tous les personnages spatiaux réels sont interdits en tant qu'infixes, mais sans surprise Unicode contient quelque chose qui peut être piraté dans le but d'abus.
leftaroundabout
11

Si cela ne vous dérange pas un opérateur supplémentaire, vous pouvez utiliser à &partir de Data.Function. En supposant que majorc'est une fonction Key -> Scale, vous pourriez écrire C & major. Cela produit une Scalevaleur:

Prelude Data.Function> :t C & major
C & major :: Scale
Mark Seemann
la source
4

Il existe déjà plusieurs bonnes réponses, mais voici une solution de style de passage de continuation qui peut être utile (peut-être pas pour cet exemple particulier, mais dans d'autres contextes où une sorte de syntaxe d'application inverse est souhaitée).

Avec des définitions standard pour certains types de domaines problématiques:

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)

vous pouvez introduire un type passant-continuation:

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

et écrivez les types primitifs de création de notes pour créer des Conttypes comme ceci:

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

Ensuite, les fonctions de construction d'échelle, de note et d'accords peuvent résoudre les Conts en types simples sous l'une ou l'autre forme de suffixe (c'est-à-dire en tant que continuations à transmettre à la Cont):

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

note :: Note -> Note
note = id

ou sous forme de préfixe (c'est-à-dire en prenant Conts comme arguments):

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

Maintenant, vous pouvez écrire:

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

Notez que clui - même n'a pas d' Showinstance, mais en a c note.

Avec une modification du Notetype, vous pourriez facilement prendre en charge les doubles accidents (par exemple c sharp sharp, distincts de d), etc.

KA Buhr
la source
Agréable. En fait, j'ai essayé de résoudre mon problème avec, Contcependant, j'ai essayé de le coller aux constructeurs A | B | C ...au lieu d'utiliser des fonctions. Je n'ai pas pu faire fonctionner cela et je ne comprends toujours pas pourquoi, étant donné que les constructeurs de valeur ne sont que des fonctions. Si je peux coller une fonction devant mes touches, beaucoup de choses deviennent possibles. Si la fonction est flip ($)alors j'obtiens votre modèle flip ($) B :: Cont Key r. Mon original aScale = scale C Majorn'est pas très différent.
Martin Drautzburg
3

Mais je ne peux pas limiter les clés à la construction des échelles. Ils sont également nécessaires pour d'autres choses (par exemple la construction d'accords). Key doit également être une instance de Show.

Vous pouvez utiliser des classes de types pour contourner intelligemment ce problème:

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

Maintenant, vous pouvez également utiliser les lettres minuscules pour d'autres types en définissant des instances appropriées.

Joseph Sible-Reinstate Monica
la source