在Haskell中定义枚举的更好方法

43 enums haskell

我想要一种数据类型来表示可以通过特定名称寻址的有限整数集.我认为最好的方法是使用Enum.

但是,有一个小问题.我知道定义Enum的唯一方法是这样的:

data MyDataType = Foo | Bar | Baz

instance Enum MyDataType 
 toEnum 0 = Foo
 toEnum 1 = Bar
 toEnum 2 = Baz

 fromEnum Foo = 0
 fromEnum Bar = 1
 fromEnum Baz = 2 
Run Code Online (Sandbox Code Playgroud)

请注意,我必须重复两次相同的对 - 一次定义整数到枚举映射,另一次定义枚举到整数映射.

有没有办法避免这种重复?

Rob*_*een 52

data MyDataType = Foo | Bar | Baz deriving (Enum)
Run Code Online (Sandbox Code Playgroud)


aug*_*tss 32

instance Enum MyDataType where
    fromEnum = fromJust . flip lookup table
    toEnum = fromJust . flip lookup (map swap table)
table = [(Foo, 0), (Bar, 1), (Baz, 2)]
Run Code Online (Sandbox Code Playgroud)

  • 我知道我不应该关心,但是这会被ghc优化成有效的东西吗? (15认同)

Gre*_*ber 13

接受的解决方案的问题是编译器不会告诉您何时丢失表中的枚举.该deriving Enum解决方案是伟大的,但如果你想拥有数字任意映射将无法正常工作.另一个答案表明泛型或模板Haskell.这通过使用来跟进Data.

{-# Language DeriveDataTypeable #-}
import Data.Data
data MyDataType = Foo | Bar | Baz deriving (Eq, Show, Data, Typeable)

toNumber enum = case enum of
   Foo -> 1
   Bar -> 2
   Baz -> 4
Run Code Online (Sandbox Code Playgroud)

toNumber添加新构造函数时,我们将在案例映射中获得编译器警告.

现在我们只需要能够将代码转换为数据,以便映射可以自动反转.这里我们生成table在接受的解决方案中提到的相同内容

table = map (\cData -> let c = (fromConstr cData :: MyDataType) in (c, toNumber c) )
      $ dataTypeConstrs $ dataTypeOf Foo
Run Code Online (Sandbox Code Playgroud)

您可以填写Enum与已接受答案中相同的课程.没有提到你也可以填写Bounded课程.