Encontrei um pequeno problema estético no meu projecto musical e isso tem-me incomodado há já algum tempo.
Tenho um tipo data Key = C | D | ...
e posso construir uma escala
a partir de um Key
e um Mode
. O Modo
distingue entre, por exemplo, uma escala maior e uma menor.
Posso definir o tipo Modo
como uma função de Key
a Scale
. Nesse caso os modos terão nomes em minúsculas (o que é bom) e posso obter uma Escala como esta
aScale = major C
Mas os músicos não'não falam assim. Referem-se a esta escala como a escala C maior, não a escala major C.
**O que eu quero***
Idealmente I'gostaria de escrever
aScale = C major
Isto é de todo possível?
**O que eu tentei***
Posso fazer "Key" uma função que constrói uma "Escala" a partir de um "Modo", para que eu possa escrever
aScale = c Major
Mas não posso confinar-me à construção de balanças. São necessárias também para outras coisas (por exemplo, construção de corda). Também "Chaves" devem ser um exemplo de "Show".
Posso colocar o Modo
após o Key
quando utilizo uma função extra (ou construtor de valores):
a Escala = escala C maior
com escala :: Chave -> Modo -> Escala
Mas a palavra extra escala parece ruidosa e ao contrário do seu nome, escala
é'não está realmente preocupada com escalas. A parte inteligente está em major
, escala
é realmente apenas flip ($)
.
Utilizando um newtype Mode = Major | Minor ... não muda muito, excepto que a
escala` precisa de ser mais inteligente:
aScale = scale C Major
Solução 1:
Use isto
data Mode = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode
Agora pode escrever (com C maiúsculo e M maiúsculo)
aScale = C Major
Solução 2a:
Isto também é possível
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
data Scale = Scale Key Mode
Agora escreve
aScale = Scale C Major
Solução 2b:
Isto também é possível
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
type Scale = (Key, Mode)
Agora escreve
aScale = (C, Major)
Aqui'é uma solução caprichosa que eu não'não recomendo realmente, mas parece muito "musical":
infix 8 ♮
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
-- ≡ flip ($)
Depois pode escrever
> C♮ major :: Scale
Claro que, onde isto se destina realmente é que também teria F♯ minor
e B♭ major
etc.
Se não se importar com um operador extra, pode utilizar 'não se importa com um operador extra, pode utilizar
&de
Data.Function. Assumindo que
majoré uma função
Key -> Scale', poderia escrever `C & major'. Isso produz um valor "Escala":
Prelude Data.Function> :t C & major
C & major :: Scale
Il y a déjà plusieurs bonnes réponses, mais voici une solution de style "continuation passing" qui peut être utile (peut-être pas pour cet exemple particulier, mais dans d'autres contextes où une sorte de syntaxe d'application inversée 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 de passage en continuation :
type Cont a r = (a -> r) -> r
et écrire les types primitifs de construction de notes pour construire des types "Cont" comme ça :
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 de gammes, de notes et d'accords peuvent résoudre les "Cont" en types simples sous la forme de postfix (c'est-à-dire comme des suites à passer au "Cont") :
major, minor :: Note -> Scale
major n = Scale n Major
minor n = Scale n Minor
note :: Note -> Note
note = id
ou la forme du préfixe (c'est-à-dire en prenant les "Cont" 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 "c" lui-même n'a pas d'instance "Show", mais "c note" en a une.
Avec une modification du type Note
, vous pourriez facilement supporter les doubles altérations (par exemple, c dièse dièse
, distinct de d
), etc.
Mas não posso limitar as Chaves à construção de Escalas. São necessárias também para outras coisas (por exemplo, construção de cordas). Também a Chave deve ser um exemplo de Show.
Pode usar as teclas para trabalhar inteligentemente em torno disso:
{-# 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
Agora, também pode utilizar as letras minúsculas para outros tipos, definindo instâncias apropriadas.