有没有机会写“ C major”而不是“ major C”?


39

我在音乐项目中遇到了一个美学上的小问题,并且困扰了我一段时间。

我有一个类型data Key = C | D | ...,可以ScaleKey和构造一个Mode。该Mode例如主要的和次要的规模进行了区分。

我可以将Mode类型定义为从Keyto 的函数Scale。在这种情况下,模式将使用小写名称(可以),我可以得到一个这样的Scale

aScale = major C

但是音乐家不会这样说话。他们将此比例称为C大尺度,而不是C大尺度。

我想要的是

理想情况下,我想写

aScale = C major

这有可能吗?

我尝试了什么

我可以创建Key一个Scale从构造a的函数Mode,所以我可以写

aScale = c Major

但是我不能将关键限制在构建音阶上。其他事物也需要它们(例如,构建和弦)。也Key应该是的实例Show


当我使用额外的函数(或值构造函数)时,可以放在Mode后面Key

aScale = scale C majorscale :: Key -> Mode -> Scale

但是额外的单词标度看起来很吵,并且与名称相反,它scale实际上与标度无关。聪明的地方在于majorscale确实是公正flip ($)


使用a newtype Mode = Major | Minor ...并不会带来太大变化,只是scale需要更加智能:

aScale = scale C Major

3
我发现自己过去想要非常相似的语法,但是TBH不值得。随便去吧major C

4
就像音乐上的怪癖一样:“键”是该数据类型的误导性名称,因为例如C大调和C小调在标准术语中是不同的键。“ PitchClass”将是该类型的更准确的名称。
PLL

2
@PLL确实,我很难找到C,C#,D的好名字……我知道Euterpea使用PitchClass。它比Key更正确,但根本不是“音乐”。现在,我正在玩称其为Root或Tonic的想法,尽管这仅表示和弦和音阶。音乐家怎么称呼这东西-没有八度音阶的音符?
Martin Drautzburg

4
@MartinDrautzburg:我不会说音调课是不合音乐的-绝不是程序员讲的,它至少在20世纪中叶就已经在音乐理论中被确立为“没有八度音阶的音符”。在音乐技术理论的背景之外,这不是很常见,但这只是因为在日常使用中并不需要真正区分“音高”和“没有八度的音高”之间的精确区别,并且在需要时通常很清楚从上下文。但是,“ Root”或“ Tonic”听起来似乎更熟悉,尽管不太精确。
PLL

1
不,因为反之亦然,程序员
沉迷

Answers:


29

解决方案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

解决方案2a:

这也是可能的

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)

IMO结合解决方案2将为您提供良好的服务。投降haskell的语法,并在其中添加干净的域模型。如果您愿意,事情会做得令人愉快
luqui

16

这是我不怎么推荐的一种异想天开的解决方案,但是看起来很“音乐”:

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

那你可以写

> C major :: Scale

当然,在这真的是针对的是,你还必须F♯ minorB♭ major等。


1
我想知道是否有像操作员一样允许不间断的空间:)
chepner

26
@chepner实际上是:U + 2800 BRAILLE PATTERN BLANK可以用作中缀。不用说,这是一个可怕的主意……所有实际的空格字符都被禁止使用中缀,但是毫无疑问,Unicode包含可以被滥用的东西。

11

如果您不介意使用多余的运算符,则可以使用&from 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

现在,您也可以通过定义适当的实例,将小写字母用于其他类型。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.