5

私は、開発者が Minitel (フランスの videotex 端末) を制御できるようにするライブラリに取り組んでいます。

多くの定数値があり、Haskell でそれらを管理する最良の方法を知りたいです。初心者の間でよくある質問ですが、満足のいく答えが見つかりません。

私のプロジェクトを見ることができます (注:はい、1つのモジュールだけに定数が多すぎます。それが私が取り組んでいることです;-))

現在、モジュールを として保持しています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 から始まらないとすぐに見苦しくなります。また、エラーが発生しやすく、1 つの値を省略または追加すると、次の名前の値がプラスまたはマイナスになります。マイナス 1:

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)。

このケースを処理するための他のより良い方法はありますか?

4

2 に答える 2