代数数据类型(预期)名称冲突..该怎么办?

时间:2019-04-25 23:59:47

标签: haskell ghc

我想在Haskell中实现自己的小音乐理论模块。

从便笺(Note)开始就很有意义,然后我遇到了这个令人讨厌的句法问题,我不知道真正的Haskellers是如何处理的。

data Note = Sharp NoteS | Flat NoteF deriving (Show)

data NoteS = 
    C | SC | D | SD | E | F | SF | G | SG | B
    deriving (Ord,Show,Eq)

data NoteF = 
    C | FD | D | FE | E | F | FG | G | FB | B
    deriving (Ord,Show,Eq)

instance Eq Note where
    (==) (NoteS n1) (NoteS n2) = n1 == n2
    (==) (NoteF n1) (NoteF n2) = n1 == n2
    (==) (NoteS n1) (NoteF n2) = ???
    (==) (NoteF n1) (NoteS n2) = ???
    ...

flatToSharp :: Note -> NoteS
sharpToFlat :: Note -> NoteF

众所周知,尖锐的C和扁平的D通常是同义词,但是有时根据上下文,优选使用一个。因此,我希望利用NoteSNoteF都是Ord实例的事实(例如,用于间隔计算)。但是在两种表示形式中,普通音符(C,D,E,F ...)在两种类型中的名称相同。

现在我可以想办法“破解”这个句法问题了。但这可能具有丑陋的语法含义或运行时含义(例如,使用字符串而不是类型,使用大量测试和错误检查等)。

这是我向Haskell专业人员提出的问题...如果不对Haskell的“命名空间”问题有太多让步,我将如何本着我的想法去做?

我尝试了{-# LANGUAGE DuplicateRecordFields #-},但显然并不能帮助工会。

2 个答案:

答案 0 :(得分:7)

如果必须将数据表示保留在那里,则Haskell中的标准命名间隔机制是模块。所以你可以写

module Sharps where data NoteS = ...

module Flats where data NoteF = ...

module Main where
import Sharps as S
import Flats as F

(当然,请不要忘记,对于GHC,每个模块都必须以适当的文件名进入其自己的文件中。)然后,在Main中,您可以引用Sharps.C或{{ 1}}获得S.C构造函数,而NoteSFlats.C获得F.C构造函数。

但是我可以提出其他解决方案吗?如何命名自然音符,并简单地使用一种类型来记录您从那里走过的锐度或平坦度呢?毕竟,我敢肯定,您最终将要处理双锐器和双扁平器。所以:

NoteF

(您还可以选择其他许多数据表示形式。)

答案 1 :(得分:5)

另一个想法是使用模式同义词。您可以使用一种类型来表示笔记,并可以使用同义词重命名其中的一些。

https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#pattern-synonyms

{-# LANGUAGE PatternSynonym #-}

data Note = C | SC | D | SD | E | F | SF | G | SG | B

pattern FD = SC
pattern FE = SD
pattern FG = SF
pattern FB = SG