如何处理 Haskell 中的大量常量?
How to handle lots of constants in Haskell?
我正在开发一个允许开发人员控制 Minitel(法语
可视图文终端)。
我有很多不变的价值观,我想知道最好的管理方法
他们 Haskell。这是初学者中的一个常见问题,但我还没有找到
令人满意的答案。
You can have a look at my project
(注意:是的,只有一个模块中有太多常量,这就是我正在做的事情;-))
我目前有模块将它们保持为 name = value
。虽然它有效,但我
想知道是否可以完善或者我做的是否正确。
aNUL = 0x00 -- Null
-- ...
aUS = 0x1f -- Unit Separator
这个方法有一个小缺点:不能使用模式匹配,你需要
如果您想保留名称,请使用守卫:
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [0x19] = False -- eSS2
completeReturn [0x1b, 0x5b, 0x32] = False -- eESC, eCSI, 0x32
completeReturn [0x1b, 0x5b, 0x34] = False -- eESC, eCSI, 0x34
completeReturn [0x19, 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
如果你不想让 GHC 对你大喊大叫,你也必须使用 GHC 选项
缺少签名或类型默认值:
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
我曾经用 data deriving Enum
尝试过使用技巧来补偿
未定义的值,但一旦值不从 0 开始,它就会变得丑陋。
它也很容易出错,如果你省略或添加一个值,下面的名称将
它们的值加一或减一:
data ASCII = NUL -- ^ 0x00, Null
-- ...
| US -- ^ 0x1f, Unit Separator
deriving (Enum, Show, Eq, Ord)
data C0 = NUL -- ^ 0x00, NULl
| Res01 -- ^ 0x01, undefined value
-- ...
| APA -- ^ 0x1f, Activate Position Address
deriving (Enum, Show, Eq, Ord)
data SSCFS = Res00 | Res01 | Res02 | Res03 | Res04 | Res05 | Res06 | Res07
-- ...
| Res38 | Res39 | Res3A | Res3B | Res3C | Res3D | Res3E | Res3F
| ABK -- ^ 0x40, Alpha BlacK
-- ...
| RMS -- ^ 0x5f
deriving (Enum, Show, Eq, Ord)
这个解决方案有一个缺点:你不能轻易地混合列表中的值
因为它们的类型不同:
codes = [ASCII.NUL, ASCII.SOH, C0.APB, C0.APF, 0x24] -- Error!
我想到了另一个解决方案:
class Value a where
value :: a -> Int
-- ASCII codes
data ASCII = NUL | SOH | STX | ETX {- ... -} deriving Show
instance Value ASCII where
value NUL = 0
value SOH = 1
-- ...
-- C0 codes
data C0 = APB | APF | APD | APU {- ... -} deriving Show
instance Value C0 where
value APB = 10
value APF = 11
-- ...
-- Mini type
data Mini = ASCII ASCII | C0 C0 | Literal Int deriving Show
instance Value Mini where
value (ASCII code) = value code
value (C0 code) = value code
value (Literal int) = int
codes = [ASCII NUL, C0 APB, Literal 0x20]
main = do
print (fmap value codes)
对于这个解决方案,我必须注意构造函数不要重叠。为了
例如,NUL、SO 和 SI 在 ASCII 和 C0 中都存在(幸运的是,它们给出了
相同的值:-))。我可以通过仅在 ASCII 中定义它们来处理这种情况
例子。使用合格的导入会使事情变得更丑陋 (ASCII ASCII.NUL
)。
您是否看到其他更好的方法来处理这种情况?
想到的第二件事(在一些丑陋到无法思考之后)是使用 Enum
而不总是推导 它。所以你可以使用你描述的相同 ASCII
,但是
instance Enum ASCII where
fromEnum a = case a of
NUL -> 0x00
...
US -> 0x1f
toEnum a = case a of
0x -> NUL
0x1f -> US
我不熟悉模板 Haskell,但我敢猜测您几乎肯定可以使用它编写一些东西,让您将单向转换 table 转换为fromEnum
和 toEnum
.
如果你有 ghc 7.8,一个新的语言扩展 pattern synonyms (see section 7.3.8) elegantly solves this problem. Pattern synonyms are enabled with a LANGUAGE pragma 或 -XPatternSynonyms
标志。
{-# LANGUAGE PatternSynonyms #-}
模式同义词定义以pattern
为前缀
pattern NUL = 0x00
pattern SSC = 0x19
pattern ESC = 0x1b
pattern US = 0x1f
pattern CSI = 0x5b
我们可以根据这些模式编写您的示例。
type MString = [Int]
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [SSC] = False -- eSS2
completeReturn [ESC , CSI , 0x32] = False -- eESC, eCSI, 0x32
completeReturn [ESC , CSI , 0x34] = False -- eESC, eCSI, 0x34
completeReturn [SSC , 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
模式同义词是双向的,所以我们也可以用它们来构造表达式。
completeReturn [SSC]
您可以编写捕获变量的模式同义词。
pattern EscCsi x = [ESC , CSI , x]
并将它们用作模式匹配的构造函数
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [SSC] = False -- eSS2
completeReturn (EscCsi 0x32) = False -- eESC, eCSI, 0x32
completeReturn (EscCsi 0x34) = False -- eESC, eCSI, 0x34
completeReturn [SSC , 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
并用于构造表达式。
completeReturn (EscCsi 0x7e)
我正在开发一个允许开发人员控制 Minitel(法语 可视图文终端)。
我有很多不变的价值观,我想知道最好的管理方法 他们 Haskell。这是初学者中的一个常见问题,但我还没有找到 令人满意的答案。
You can have a look at my project (注意:是的,只有一个模块中有太多常量,这就是我正在做的事情;-))
我目前有模块将它们保持为 name = value
。虽然它有效,但我
想知道是否可以完善或者我做的是否正确。
aNUL = 0x00 -- Null
-- ...
aUS = 0x1f -- Unit Separator
这个方法有一个小缺点:不能使用模式匹配,你需要 如果您想保留名称,请使用守卫:
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [0x19] = False -- eSS2
completeReturn [0x1b, 0x5b, 0x32] = False -- eESC, eCSI, 0x32
completeReturn [0x1b, 0x5b, 0x34] = False -- eESC, eCSI, 0x34
completeReturn [0x19, 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
如果你不想让 GHC 对你大喊大叫,你也必须使用 GHC 选项 缺少签名或类型默认值:
{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
我曾经用 data deriving Enum
尝试过使用技巧来补偿
未定义的值,但一旦值不从 0 开始,它就会变得丑陋。
它也很容易出错,如果你省略或添加一个值,下面的名称将
它们的值加一或减一:
data ASCII = NUL -- ^ 0x00, Null
-- ...
| US -- ^ 0x1f, Unit Separator
deriving (Enum, Show, Eq, Ord)
data C0 = NUL -- ^ 0x00, NULl
| Res01 -- ^ 0x01, undefined value
-- ...
| APA -- ^ 0x1f, Activate Position Address
deriving (Enum, Show, Eq, Ord)
data SSCFS = Res00 | Res01 | Res02 | Res03 | Res04 | Res05 | Res06 | Res07
-- ...
| Res38 | Res39 | Res3A | Res3B | Res3C | Res3D | Res3E | Res3F
| ABK -- ^ 0x40, Alpha BlacK
-- ...
| RMS -- ^ 0x5f
deriving (Enum, Show, Eq, Ord)
这个解决方案有一个缺点:你不能轻易地混合列表中的值 因为它们的类型不同:
codes = [ASCII.NUL, ASCII.SOH, C0.APB, C0.APF, 0x24] -- Error!
我想到了另一个解决方案:
class Value a where
value :: a -> Int
-- ASCII codes
data ASCII = NUL | SOH | STX | ETX {- ... -} deriving Show
instance Value ASCII where
value NUL = 0
value SOH = 1
-- ...
-- C0 codes
data C0 = APB | APF | APD | APU {- ... -} deriving Show
instance Value C0 where
value APB = 10
value APF = 11
-- ...
-- Mini type
data Mini = ASCII ASCII | C0 C0 | Literal Int deriving Show
instance Value Mini where
value (ASCII code) = value code
value (C0 code) = value code
value (Literal int) = int
codes = [ASCII NUL, C0 APB, Literal 0x20]
main = do
print (fmap value codes)
对于这个解决方案,我必须注意构造函数不要重叠。为了
例如,NUL、SO 和 SI 在 ASCII 和 C0 中都存在(幸运的是,它们给出了
相同的值:-))。我可以通过仅在 ASCII 中定义它们来处理这种情况
例子。使用合格的导入会使事情变得更丑陋 (ASCII ASCII.NUL
)。
您是否看到其他更好的方法来处理这种情况?
想到的第二件事(在一些丑陋到无法思考之后)是使用 Enum
而不总是推导 它。所以你可以使用你描述的相同 ASCII
,但是
instance Enum ASCII where
fromEnum a = case a of
NUL -> 0x00
...
US -> 0x1f
toEnum a = case a of
0x -> NUL
0x1f -> US
我不熟悉模板 Haskell,但我敢猜测您几乎肯定可以使用它编写一些东西,让您将单向转换 table 转换为fromEnum
和 toEnum
.
如果你有 ghc 7.8,一个新的语言扩展 pattern synonyms (see section 7.3.8) elegantly solves this problem. Pattern synonyms are enabled with a LANGUAGE pragma 或 -XPatternSynonyms
标志。
{-# LANGUAGE PatternSynonyms #-}
模式同义词定义以pattern
pattern NUL = 0x00
pattern SSC = 0x19
pattern ESC = 0x1b
pattern US = 0x1f
pattern CSI = 0x5b
我们可以根据这些模式编写您的示例。
type MString = [Int]
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [SSC] = False -- eSS2
completeReturn [ESC , CSI , 0x32] = False -- eESC, eCSI, 0x32
completeReturn [ESC , CSI , 0x34] = False -- eESC, eCSI, 0x34
completeReturn [SSC , 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
模式同义词是双向的,所以我们也可以用它们来构造表达式。
completeReturn [SSC]
您可以编写捕获变量的模式同义词。
pattern EscCsi x = [ESC , CSI , x]
并将它们用作模式匹配的构造函数
completeReturn :: MString -> Bool
completeReturn [] = False
completeReturn [SSC] = False -- eSS2
completeReturn (EscCsi 0x32) = False -- eESC, eCSI, 0x32
completeReturn (EscCsi 0x34) = False -- eESC, eCSI, 0x34
completeReturn [SSC , 0x4b] = False -- eSS2, 0x4b ; cedilla
completeReturn _ = True
并用于构造表达式。
completeReturn (EscCsi 0x7e)