代数数据类型(预期)名称冲突..怎么办?
Algebraic data type (intended) name collision.. how to?
我想在 Haskell 中实现自己的小音乐理论模块。
从注释 (Note
) 开始是有道理的,然后我 运行 陷入了这个讨厌的句法问题,我不知道真正的 Haskell 人如何处理它。
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 通常是同义词,但有时根据上下文更愿意使用其中一个。所以我希望利用 NoteS
和 NoteF
都是 Ord
的实例这一事实(例如用于区间计算)。但是在这两种表示中,普通音符 (C,D,E,F...) 在两种类型中具有相同的名称。
现在我可以想办法 "hack" 解决这个句法问题了。但它要么具有丑陋的语法含义,要么具有 运行 时间含义(例如,使用字符串而不是类型,大量测试和错误检查,...)。
所以这是我要问 Haskell 专业人士的问题...我将如何本着我的想法去做,而又不对 Haskell 的 "namespace" 问题做出太多让步?
我试过 {-# LANGUAGE DuplicateRecordFields #-}
但显然它对工会没有帮助。
如果您必须保持现有的数据表示形式,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
或S.C
获取 NoteS
构造函数,Flats.C
或 F.C
获取 NoteF
构造函数。
但我可以提出不同的解决方案吗?如何命名自然音符,并简单地使用一种类型来记录您从那里走了多高或多低?毕竟,您最终会想要处理双升号和双降号,我敢肯定。所以:
data Natural = A | B | C | D | E | F | G
data Note = Note
{ natural :: Natural
, offset :: Int -- positive for sharp, negative for flat, say
}
(您还可以做出许多其他数据表示选择。)
另一个想法是使用模式同义词。您可以使用一个类型来表示注释,并使用同义词来重命名其中的一些注释。
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
我想在 Haskell 中实现自己的小音乐理论模块。
从注释 (Note
) 开始是有道理的,然后我 运行 陷入了这个讨厌的句法问题,我不知道真正的 Haskell 人如何处理它。
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 通常是同义词,但有时根据上下文更愿意使用其中一个。所以我希望利用 NoteS
和 NoteF
都是 Ord
的实例这一事实(例如用于区间计算)。但是在这两种表示中,普通音符 (C,D,E,F...) 在两种类型中具有相同的名称。
现在我可以想办法 "hack" 解决这个句法问题了。但它要么具有丑陋的语法含义,要么具有 运行 时间含义(例如,使用字符串而不是类型,大量测试和错误检查,...)。
所以这是我要问 Haskell 专业人士的问题...我将如何本着我的想法去做,而又不对 Haskell 的 "namespace" 问题做出太多让步?
我试过 {-# LANGUAGE DuplicateRecordFields #-}
但显然它对工会没有帮助。
如果您必须保持现有的数据表示形式,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
或S.C
获取 NoteS
构造函数,Flats.C
或 F.C
获取 NoteF
构造函数。
但我可以提出不同的解决方案吗?如何命名自然音符,并简单地使用一种类型来记录您从那里走了多高或多低?毕竟,您最终会想要处理双升号和双降号,我敢肯定。所以:
data Natural = A | B | C | D | E | F | G
data Note = Note
{ natural :: Natural
, offset :: Int -- positive for sharp, negative for flat, say
}
(您还可以做出许多其他数据表示选择。)
另一个想法是使用模式同义词。您可以使用一个类型来表示注释,并使用同义词来重命名其中的一些注释。
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