Есть ли шанс написать «мажор» вместо «мажор С»?

39

Я столкнулся с небольшой эстетической проблемой в моем музыкальном проекте, и это беспокоило меня в течение некоторого времени.

У меня есть тип, data Key = C | D | ...и я могу построить Scaleиз а Keyи а Mode. В Modeразличает , например , крупный и незначительный масштаб.

Я могу определить Modeтип как функцию от Keyдо Scale. В этом случае режимы будут иметь имена в нижнем регистре (это нормально), и я могу получить такой масштаб

aScale = major C

Но музыканты так не разговаривают. Они называют эту шкалу C-мажорной шкалой, а не мажорной C- шкалой.

Что я хочу

В идеале я бы хотел написать

aScale = C major

Это вообще возможно?

Что я пробовал

Я могу сделать Keyфункцию, которая строит Scaleиз Mode, так что я могу написать

aScale = c Major

Но я не могу ограничивать Ключи конструированием Весов. Они нужны и для других вещей (например, для создания аккордов ). Также Keyдолжен быть экземпляр Show.


Я могу поставить Modeпосле того, Keyкогда я использую дополнительную функцию (или конструктор значений):

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

Но лишнее слово « весы» выглядит шумно и, вопреки своему названию, на scaleсамом деле не касается весов. Интеллектуальная часть находится в major, scaleна самом деле просто flip ($).


Использование действительно newtype Mode = Major | Minor ...ничего не меняет, за исключением того, что scaleдолжно быть более умным:

aScale = scale C Major
Мартин Драуцбург
источник
3
В прошлом я сам нуждался в крайне похожем синтаксисе, но TBH того не стоит. Просто иди с major C.
оставил около
4
Так же, как музыкальная придира: «Ключ» - вводящее в заблуждение имя для этого типа данных, поскольку, например, до-мажор и до-минор - разные ключи в стандартной терминологии. «PitchClass» будет более точным именем для типа.
PLL
2
@PLL Действительно, у меня проблемы с поиском подходящего имени для C, C #, D ... Я знаю, что Euterpea использует PitchClass. Это более правильно, чем Key, но вовсе не «музыкальный». Прямо сейчас я играю с идеей назвать это Root или Tonic, хотя это предлагает только Аккорды и Весы. Как, черт возьми, музыканты называют эту вещь - нота без октавы?
Мартин Драуцбург
4
@MartinDrautzburg: Я бы не сказал, что урок тангажа не является музыкальным - он не просто говорит программистом, он был создан в теории музыки как «нота без октавы», по крайней мере, примерно с середины 20-го века. Это не очень часто встречается вне технических контекстов теории музыки, но это только потому, что точное различие между «высотой» и «высотой без октавы» на самом деле не требуется часто в повседневном использовании, и когда это необходимо, обычно это ясно из контекста. Но «Root» или «Tonic» звучат хорошо, как несколько более знакомые термины, если не так точно.
ФАПЧ
1
Нет, потому что это не работает наоборот, программист
занимается

Ответы:

29

Решение 1:

Использовать этот

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

Теперь вы можете написать (с заглавной С и заглавной М)

aScale = C Major

Решение 2а:

Это тоже возможно

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

data Scale = Scale Key Mode  

Теперь пишешь

aScale = Scale C Major

Решение 2b:

Это тоже возможно

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

type Scale = (Key, Mode)  

Теперь пишешь

aScale = (C, Major)
Elmex80s
источник
IMO с решением 2 будет вам полезен. Сдайтесь синтаксису haskell и сделайте в нем чистую модель вашего домена. Вещи могут быть приятными, если вы делаете
Luqui
16

Вот одно причудливое решение, которое я не очень рекомендую, но выглядит очень «музыкально»:

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

Тогда вы можете написать

> C major :: Scale

Конечно, на что это действительно направлено, так это то, что вы бы тоже имели F♯ minorи B♭ majorт.д.

leftaroundabout
источник
1
Интересно, есть ли что-то вроде неразрывного пробела, который разрешен в качестве оператора :)
chepner
26
@chepner на самом деле да: U + 2800 БРАЙЛЛ ШАБЛОН ПУСТА можно использовать как инфикс. Само собой разумеется, это ужасная идея ... Все действительные символы пробела запрещены как инфиксы, но неудивительно, что Unicode содержит что-то, что может быть взломано в целях злоупотребления.
оставил около
11

Если вы не возражаете против дополнительного оператора, вы можете использовать &с Data.Function. Предполагая, что majorэто функция Key -> Scale, вы можете написать C & major. Это дает Scaleзначение:

Prelude Data.Function> :t C & major
C & major :: Scale
Марк Симанн
источник
4

Уже есть несколько хороших ответов, но вот решение в стиле передачи продолжения, которое может быть полезным (возможно, не для этого конкретного примера, но в других контекстах, где требуется своего рода синтаксис обратного приложения).

Со стандартными определениями для некоторых типов проблемных областей:

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)

Вы можете ввести тип продолжения:

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

и напишите примитивные типы создания заметок для создания Contтипов следующим образом:

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

Затем функции построения, примечания и создания аккордов могут преобразовывать Conts в простые типы в любой форме постфикса (т. Е. Как продолжения, передаваемые в Cont):

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

note :: Note -> Note
note = id

или префикс формы (т. е. принимая Conts в качестве аргументов):

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

Теперь вы можете написать:

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

Обратите внимание, что cсам по себе не имеет Showэкземпляра, но c noteесть.

С изменением Noteтипа вы можете легко поддерживать двойные случайные (например c sharp sharp, отличные от d) и т. Д.

К. А. Бур
источник
Ницца. Я действительно пытался решить мою проблему, Contоднако, я пытался привязать ее к конструкторам A | B | C ...вместо использования функций. Я не мог заставить это работать, и я все еще не понимаю почему, учитывая, что конструкторы значений - это просто функции. Если я могу поставить функцию перед своими ключами, многое станет возможным. Если функция, flip ($)то я получаю ваш шаблон flip ($) B :: Cont Key r. Мой оригинал aScale = scale C Majorмало чем отличается.
Мартин Драуцбург
3

Но я не могу ограничивать Ключи конструированием Весов. Они нужны и для других вещей (например, для создания аккордов). Также Key должен быть экземпляром Show.

Вы можете использовать классы типов, чтобы ловко обойти это:

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

Теперь вы можете использовать строчные буквы и для других типов, определив соответствующие экземпляры.

Джозеф Сибл-Восстановить Монику
источник