Haskell中的C风格枚举? [英] C-style enum in Haskell?
问题描述
在C中,我们以这种方式定义枚举:
In C, we define enum this way:
enum E {
E0,
E1,
E2 = 3,
E3
};
注意 E2 = 3
表达式,枚举输入结果 E0 == 0,E1 == 1,E2 == 3,E3 == 4
。
Note E2 = 3
expression, the enum type result in E0 == 0, E1 == 1, E2 == 3, E3 == 4
.
在Haskell中,我们无法在声明中指定枚举。实现不连续枚举的唯一方法是手动执行 Enum
类。
In Haskell, we have no way to specify the enumeration in the declaration. The only way to implement discontinuous enumeration is implementing Enum
class manually.
有没有办法这样做?
我写了一个演示使用Template Haskell生成枚举
实例。
I've write a demo using Template Haskell to generate the Enum
instance.
data E = E0
| E1
| E2_3
| E3
deriving Show
enum ''E
我想知道如果有图书馆试图填补这个差距?
I wonder if there are libraries trying to fill this gap?
推荐答案
你可以鞭打一些小小的简单的使用Template Haskell的 reifyAnnotations
功能。
You can whip up something small & simple using Template Haskell's reifyAnnotations
feature.
首先,我们需要定义一个注释类型来保存枚举值: / p>
First, we need to define an annotation type to hold enum values:
{-# LANGUAGE DeriveDataTypeable #-}
module Def where
import Data.Data
data EnumValue = EnumValue Int deriving (Typeable, Data)
其次,我们需要一些TH代码来消费这些注释,并将它们变成枚举
实例定义:
Second, we need a bit of TH code to consume these annotations and turn them into Enum
instance definitions:
{-# 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])|]
然后最后我们可以使用它(通过小的解决方法,以确保使用情况在新的声明组中):
And then finally we can use it (via a small workaround to make sure the usage is in a new declaration group):
{-# 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
示例用法:
*AnnotatedEnumExample> map fromEnum [E1, E2, E42, E43]
[1,2,42,43]
*AnnotatedEnumExample> map toEnum [1, 2, 42, 43] :: [E]
[E1,E2,E42,E43]
这篇关于Haskell中的C风格枚举?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!