Haskell 有没有机会写一封信;C大调“;而不是",;大C";?
我在我的音乐项目中遇到了一个小的美学问题,它困扰了我一段时间 我有一个类型Haskell 有没有机会写一封信;C大调“;而不是",;大C";?,haskell,dsl,Haskell,Dsl,我在我的音乐项目中遇到了一个小的美学问题,它困扰了我一段时间 我有一个类型data Key=C | D |…,我可以从键和模式构建比例。模式区分大音阶和小音阶等 我可以将模式类型定义为从键到缩放的函数。在这种情况下,模式将使用小写名称(这很好),我可以得到这样的比例 aScale = major C 但音乐家不会这样说话。他们把这个音阶称为C大调音阶,而不是C大调音阶 我想要什么 理想情况下,我会想写作 aScale = C major 这可能吗 我尝试的 我可以使键成为一个函数,从模式构造
data Key=C | D |…
,我可以从键和模式构建比例。模式
区分大音阶和小音阶等
我可以将模式
类型定义为从键
到缩放
的函数。在这种情况下,模式将使用小写名称(这很好),我可以得到这样的比例
aScale = major C
但音乐家不会这样说话。他们把这个音阶称为C大调音阶,而不是C大调音阶
我想要什么
理想情况下,我会想写作
aScale = C major
这可能吗
我尝试的
我可以使键
成为一个函数,从模式
构造一个刻度
,这样我就可以写了
aScale = c Major
> C♮ major :: Scale
但我不能将关键点局限于构建比例。其他事情也需要它们(例如构造和弦)。另外,Key
应该是Show
的一个实例
使用额外函数(或值构造函数)时,我可以将模式
置于键
之后:
aScale=C大调音阶
带scale::Key->Mode->scale
但是额外的单词scale看起来很嘈杂,与它的名字相反,scale
与scale并不真正相关。智能部分在大调
,刻度
实际上就是翻转($)
使用newtype Mode=Major | Minor…
实际上变化不大,除了scale
需要更智能:
aScale = scale C Major
解决方案1:
用这个
data Mode = Major | Minor
data Scale = C Mode | D Mode | E Mode | F Mode | G Mode | A Mode | B Mode
现在你可以写了(大写C和M)
解决方案2a:
这也是可能的
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
data Scale = Scale Key Mode
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
type Scale = (Key, Mode)
现在你写
aScale = Scale C Major
aScale = (C, Major)
解决方案2b:
这也是可能的
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
data Scale = Scale Key Mode
data Mode = Major | Minor
data Key = C | D | E | F | G | A | B
type Scale = (Key, Mode)
现在你写
aScale = Scale C Major
aScale = (C, Major)
如果您不介意增加一个运算符,您可以使用Data.Function
中的&
。假设major
是一个函数Key->Scale
,您可以编写C&major
。产生比例
值的:
Prelude Data.Function> :t C & major
C & major :: Scale
这里有一个我并不推荐但看起来很“音乐化”的异想天开的解决方案:
infix 8 ♮
(♮) :: Key -> Mode -> Scale
(♮) = (Data.Function.&)
-- ≡ flip ($)
然后你就可以写了
aScale = c Major
> C♮ major :: Scale
当然,这真正的目的是你也会有F♯ 次要
和B♭ major
等..已经有好几个好的答案,但是这里有一个延续传递样式的解决方案可能会有所帮助(可能不是针对这个特定的示例,而是在需要某种反向应用程序语法的其他上下文中)
使用某些问题域类型的标准定义:
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
然后,音阶、音符和和弦构建函数可以将Cont
解析为任意后缀形式的普通类型(即作为要传递给Cont
的延续):
或前缀形式(即,将Cont
s作为参数):
现在,你可以写:
> 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 sharp
,与d
不同),等等
但我不能将关键点局限于构建比例。其他事情也需要它们(例如构造和弦)。键还应该是Show的一个实例
您可以使用TypeClass巧妙地解决这一问题:
{-# 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
现在,通过定义适当的实例,您也可以将小写字母用于其他类型。我发现自己在过去需要非常相似的语法,但这并不值得。只需使用大调C
。就像音乐中的一句俏皮话:“Key”是该数据类型的一个误导性名称,因为例如C大调和C小调在标准术语中是不同的键。“PitchClass”将是该类型更准确的名称。@事实上,我很难找到C、C#、D的好名称。。。我知道Euterpea使用PitchClass。它比基调更正确,但根本不是“音乐”。现在我想把它叫做根音或主音,虽然这只意味着和弦和音阶。音乐家们管那玩意儿叫什么?没有八度的音符?@MartinDrautzburg:我不会说音高课没有音乐性——它不只是程序员用任何方式说话,它在音乐理论中被确定为“没有八度的音符”的意思,至少在20世纪中叶左右。这在技术音乐理论背景之外并不常见,但这只是因为“一个音高”和“一个没有八度的音高”之间的精确区别在日常使用中并不经常需要,而且当需要时,通常可以从背景中清楚地看到。但“根”或“主音”听起来更为熟悉,但不够精确。不,因为它不起作用,反之亦然,一个进入musicI的程序员想知道是否有像不间断空格这样的东西可以作为操作符:)@chepner实际上是:⠀代码>U+2800盲文模式空白可以用作中缀。不用说,这是一个可怕的想法。。。所有实际的空格字符都被禁止作为中缀,但不出所料,Unicode包含一些可以被入侵的东西。实际上,我确实试图用Cont
来解决我的问题,但是,我试图将它坚持到构造函数A | B | C…
上,而不是使用函数。我不能让它工作,我仍然不明白为什么,因为值构造函数只是函数。如果我能在我的按键前粘贴一个函数,许多