Existe alguma chance de escrever "Dó maior" em vez de "Dó maior"?

39

Eu encontrei um pequeno problema estético no meu projeto musical e ele está me incomodando há algum tempo.

Eu tenho um tipo data Key = C | D | ...e posso construir a Scalepartir de a Keye a Mode. Os Modedistingue entre, por exemplo, um grande e um menor escala.

Eu posso definir o Modetipo como uma função de Keypara 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 músicos não falam assim. Eles se referem a essa escala como a escala C maior , não a escala C maior.

O que eu quero

Idealmente, eu gostaria de escrever

aScale = C major

Isso é possível em tudo?

O que eu tentei

Eu posso criar Keyuma função que construa a Scalede a Mode, para que eu possa escrever

aScale = c Major

Mas não posso limitar as chaves à construção de escalas. Eles são necessários para outras coisas também (por exemplo, construção de acordes ). Também Keydeve ser uma instância de Show.


Eu posso colocar o Modedepois do Keyquando eu uso uma função extra (ou construtor de valor):

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

Mas a escala extra de palavras parece barulhenta e, ao contrário do nome, scalenão está realmente preocupada com escalas. A parte inteligente está dentro major, scaleé realmente justa flip ($).


Usar um newtype Mode = Major | Minor ...realmente não muda muito, exceto que scaleprecisa ser mais inteligente:

aScale = scale C Major
Martin Drautzburg
fonte
3
Eu me vi querendo uma sintaxe extremamente semelhante no passado, mas TBH não vale a pena. Apenas vá com major C.
leftaroundabout
4
Assim como uma queixa musical: “Key” é um nome enganoso para esse tipo de dados, pois, por exemplo, Dó maior e Dó menor são chaves diferentes na terminologia padrão. "PitchClass" seria um nome mais preciso para o tipo.
PLL
2
@PLL De fato, estou tendo problemas para encontrar um bom nome para C, C #, D ... Eu sei que o Euterpea usa o PitchClass. É mais correto que Key, mas não é "musical". No momento, estou brincando com a idéia de chamá-lo de Root ou Tonic, embora isso sugira apenas acordes e escalas. Como diabos os músicos chamam isso - uma nota sem oitava?
Martin Drautzburg
4
@MartinDrautzburg: Eu não diria que a aula de pitch não é musical - não é apenas a linguagem do programador, de qualquer forma, foi estabelecida na teoria da música como significando "uma nota sem oitava" desde pelo menos em meados do século XX. Não é muito comum fora dos contextos técnicos da teoria musical, mas isso ocorre apenas porque a distinção precisa entre "um tom" e "um tom sem oitava" não é realmente necessária com frequência no uso diário, e quando é necessário, geralmente é claro do contexto. Mas “Root” ou “Tonic” parecem bons, como termos um pouco mais familiares, se menos precisos.
PLL
11
Não, porque não funciona vice-versa, um programador entrando na música
Emobe 01/02

Respostas:

29

Solução 1:

Usa isto

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

Agora você pode escrever (com maiúsculas C e maiúsculas M)

aScale = C Major

Solução 2a:

Isso também é possível

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

data Scale = Scale Key Mode  

Agora você escreve

aScale = Scale C Major

Solução 2b:

Isso também é possível

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

type Scale = (Key, Mode)  

Agora você escreve

aScale = (C, Major)
Elmex80s
fonte
A IMO que acompanha a solução 2 o atenderá bem. Renda-se à sintaxe do haskell e faça dele um modelo limpo do seu domínio. As coisas podem ser agradáveis ​​se você fizer
luqui 02/02
16

Aqui está uma solução extravagante que eu realmente não recomendo, mas parece muito "musical":

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

Então você pode escrever

> C major :: Scale

Claro, onde isso é realmente direcionado é que você também teria F♯ minore B♭ majoretc.

leftaroundabout
fonte
11
Gostaria de saber se há algo como espaço sem quebra que é permitido como operador :)
chepner 31/01
26
@chepner na verdade sim: U + 2800 BRAILLE PATTERN BLANK pode ser usado como um infix. Escusado será dizer que esta é uma idéia horrível ... Todos os caracteres espaciais reais são proibidos como infixos, mas, sem surpresa, o Unicode contém algo que pode ser invadido por motivos de abuso.
leftaroundabout
11

Se você não se importa com um operador extra, pode usar a &partir de Data.Function. Supondo que majorseja uma função Key -> Scale, você poderia escrever C & major. Isso produz um Scalevalor:

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

Já existem várias respostas boas, mas aqui está uma solução de estilo de passagem contínua que pode ser útil (talvez não para este exemplo específico, mas em outros contextos em que é necessária uma espécie de sintaxe de aplicativo reverso).

Com definições padrão para alguns tipos de domínio com problemas:

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)

você pode introduzir um tipo de passagem de continuação:

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

e escreva os tipos primitivos de criação de notas para criar Conttipos assim:

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

Em seguida, as funções de escala, nota e construção de acordes podem resolver os Conttipos simples de s em qualquer forma de postfix (ou seja, como continuações a serem passadas para o Cont):

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

note :: Note -> Note
note = id

ou forma de prefixo (ou seja, usando Conts como argumentos):

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

Agora, você pode escrever:

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

Observe que cele próprio não possui uma Showinstância, mas c notepossui.

Com uma modificação no Notetipo, você pode facilmente suportar acidentes duplos (por exemplo c sharp sharp, distintos de d), etc.

KA Buhr
fonte
Agradável. Na verdade, tentei resolver meu problema, no Contentanto, tentei atendê-lo aos construtores em A | B | C ...vez de usar funções. Eu não consegui fazer isso funcionar e ainda não entendo o porquê, dado que os construtores de valor são apenas funções. Se eu conseguir colocar uma função na frente das minhas teclas, muitas coisas se tornam possíveis. Se a função for flip ($), entendo seu padrão flip ($) B :: Cont Key r. Meu original aScale = scale C Majornão é muito diferente.
Martin Drautzburg
3

Mas não posso limitar as chaves à construção de escalas. Eles são necessários para outras coisas também (por exemplo, construção de acordes). Também Key deve ser uma instância de Show.

Você pode usar classes tipográficas para solucionar isso de maneira inteligente:

{-# 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, você também pode usar as letras minúsculas para outros tipos, definindo instâncias apropriadas.

Joseph Sible-Restabelecer Monica
fonte