Haskell中的C风格枚举? [英] C-style enum in Haskell?

查看:101
本文介绍了Haskell中的C风格枚举?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆