Есть ли шанс написать «C major» вместо «major C»?

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

У меня есть тип data Key = C | D | ...и я могу построитьМасштабизКлючаиМод. «Режим» различает, например,. крупный и второстепенный масштаб.

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

aScale = major C

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

Что я хочу

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

aScale = C major

Возможно ли это вообще?

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

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

aScale = c Major

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


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

aScale = масштаб C major с scale :: Key - > Режим - > Масштаб

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


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

aScale = scale C Major
Комментарии к вопросу (5)

Решение 1:

Используйте это

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

Теперь вы можете написать (с большой буквы C и заглавной буквы M)

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)
Комментарии (1)

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

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

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

> C♮ major :: Scale

Конечно, это действительно нацелено на то, что у вас также будут «F♯ minor» и «B ₣ major» и т. Д..

Комментарии (2)

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

Prelude Data.Function> :t C & major
C & major :: Scale
Комментарии (0)

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

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

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 для простых типов в любой форме postfix (т.е., как продолжение, которое должно быть передано в 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 есть.

С модификацией типа «Примечание» вы можете легко поддерживать двойные случайные (например,., c острый острый, отличается от d) и т. д.

Комментарии (1)

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

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

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

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

Комментарии (0)