如何处理Haskell中的大量常量?

zig*_*zou 5 haskell constants pattern-synonyms

我正在开发一个库,允许开发人员控制Minitel(法国可视图文终端).

我有很多常量值,我想知道使用Haskell管理它们的最佳方法.这是初学者的常见问题,但我没有找到令人满意的答案.

你可以看看我的项目 (注意:是的,只有一个模块中有太多的常量,这就是我正在做的事情;-))

我目前有模块保持它们name = value.虽然它有效但我想知道它是否可以完善或者我是否做得对.

aNUL = 0x00 -- Null
-- ...
aUS  = 0x1f -- Unit Separator
Run Code Online (Sandbox Code Playgroud)

此方法有一个小缺点:您不能使用模式匹配,如果要保留名称,则需要使用警卫:

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
Run Code Online (Sandbox Code Playgroud)

如果您不希望GHC因丢失的签名或类型默认值而对您大喊大叫,您还必须使用GHC选项:

{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-type-defaults #-}
Run Code Online (Sandbox Code Playgroud)

我曾经尝试过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)
Run Code Online (Sandbox Code Playgroud)

此解决方案有一个缺点:您无法轻松混合列表中的值,因为它们的类型不同:

codes = [ASCII.NUL, ASCII.SOH, C0.APB, C0.APF, 0x24] -- Error!
Run Code Online (Sandbox Code Playgroud)

我想到了另一个解决方案:

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)
Run Code Online (Sandbox Code Playgroud)

对于这个解决方案,我必须注意构造函数不重叠.例如,NUL,SO和SI都存在于ASCII和C0中(幸运的是它们给出相同的值:-)).我可以通过仅以ASCII格式定义它来处理这种情况.使用合格的导入会使事情变得更加丑陋(ASCII ASCII.NUL).

您是否看到其他更好的方法来处理此案例?

Cir*_*dec 6

如果你有ghc 7.8,一个新的语言扩展模式同义词(见7.3.8节)优雅地解决了这个问题.使用LANGUAGE编译指示-XPatternSynonyms标志启用模式同义词.

{-# LANGUAGE PatternSynonyms #-}
Run Code Online (Sandbox Code Playgroud)

模式同义词定义以前缀为前缀 pattern

pattern NUL = 0x00
pattern SSC = 0x19
pattern ESC = 0x1b
pattern US  = 0x1f
pattern CSI = 0x5b
Run Code Online (Sandbox Code Playgroud)

我们可以根据这些模式编写您的示例.

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
Run Code Online (Sandbox Code Playgroud)

模式同义词是双向的,因此我们也可以使用它们来构造表达式.

completeReturn [SSC]
Run Code Online (Sandbox Code Playgroud)

您可以编写捕获变量的模式同义词.

pattern EscCsi x = [ESC , CSI , x]
Run Code Online (Sandbox Code Playgroud)

并将它们用作模式匹配的构造函数

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
Run Code Online (Sandbox Code Playgroud)

并用于构造表达式.

completeReturn (EscCsi 0x7e)
Run Code Online (Sandbox Code Playgroud)

  • @dfeuer提案是这么说的.["与`ViewPatterns`一起,我们现在可以创建看起来像常规模式的模式,以便以新方式匹配现有(可能是抽象)类型"](https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#单directionalpattern-onlysynonyms).我没试过.这与我们可以声明模式同义词的类型相同,例如`pattern NUL :: Int; 模式NUL = 0x00`,但这不起作用. (2认同)