Haskell中的C风格枚举?

wen*_*ong 4 enums haskell

在C中,我们以这种方式定义枚举:

enum E {
    E0,
    E1,
    E2 = 3,
    E3
};
Run Code Online (Sandbox Code Playgroud)

注意E2 = 3表达式,枚举类型结果E0 == 0, E1 == 1, E2 == 3, E3 == 4.

在Haskell中,我们无法在声明中指定枚举.实现不连续枚举的唯一方法是Enum手动实现类.

有没有方便的方法呢?

我使用Template Haskell 编写了一个演示来生成Enum实例.

data E = E0
       | E1
       | E2_3
       | E3
       deriving Show

enum ''E
Run Code Online (Sandbox Code Playgroud)

我想知道是否有图书馆试图填补这个空白?

Cac*_*tus 6

你可以使用Template Haskell的reifyAnnotations功能来简单小巧.

首先,我们需要定义一个注释类型来保存枚举值:

{-# LANGUAGE DeriveDataTypeable #-}
module Def where

import Data.Data

data EnumValue = EnumValue Int deriving (Typeable, Data)
Run Code Online (Sandbox Code Playgroud)

其次,我们需要一些TH代码来使用这些注释并将它们转换为Enum实例定义:

{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
module TH where

import Def
import Language.Haskell.TH.Syntax
import Language.Haskell.TH
import Control.Monad
import Data.List (mapAccumL)
import Data.Maybe

enumValues :: [(a, Maybe Int)] -> [(a, Int)]
enumValues = snd . mapAccumL (\next (x, mv) -> let v = fromMaybe next mv in (v+1, (x, v))) 0

enumFromAnns :: Name -> Q [Dec]
enumFromAnns name = do
    TyConI (DataD _ _ _ cons _) <- reify name
    eVals <- fmap enumValues $ forM cons $ \(NormalC conName []) -> do
        anns <- reifyAnnotations (AnnLookupName conName)
        let ev = case anns of
                [EnumValue ev] -> Just ev
                [] -> Nothing
        return (conName, ev)
    [d|
     instance Enum $(conT name) where
       fromEnum = $(lamCaseE [match (conP c []) (normalB $ lift v) [] | (c, v) <- eVals])
       toEnum =  $(lamCaseE [match (litP . IntegerL . fromIntegral $ v) (normalB $ conE c) [] | (c, v) <- eVals])|]
Run Code Online (Sandbox Code Playgroud)

最后我们可以使用它(通过一个小的解决方法来确保使用在新的声明组中):

{-# LANGUAGE TemplateHaskell #-}
module AnnotatedEnumExample where

import Def
import TH

data E = E1
       | E2
       | E42
       | E43
       deriving Show

{-# ANN E1 (EnumValue 1) #-}
{-# ANN E42 (EnumValue 42) #-}

-- Force new declaration group
return []

enumFromAnns ''E
Run Code Online (Sandbox Code Playgroud)

用法示例:

*AnnotatedEnumExample> map fromEnum [E1, E2, E42, E43]
[1,2,42,43]
*AnnotatedEnumExample> map toEnum [1, 2, 42, 43] :: [E]
[E1,E2,E42,E43]
Run Code Online (Sandbox Code Playgroud)