prelude functions fractional haskell enums

functions - Mejor forma de definir un Enum en Haskell



modulo prelude haskell (4)

Quiero que un tipo de datos represente un conjunto finito de enteros que pueden abordarse con nombres específicos. Me imagino que la mejor manera de hacerlo es usar Enum . Sin embargo, hay un pequeño problema. La única manera que conozco para definir un Enum es algo como esto:

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

Tenga en cuenta que tengo que repetir el mismo par dos veces, una vez cuando se define un mapeo entero a enum y el otro momento cuando se define un mapeo enum-to-integer. ¿Hay alguna manera de evitar esta repetición?

Gracias.


Dado que usted dice que los números no son generados por ninguna ley regular, podría usar programación genérica (por ejemplo, con Scrap Your Boilerplate) o Template Haskell para implementar una solución genérica a este problema. Tiendo a preferir Template Haskell porque en realidad genera código y lo compila, por lo que obtienes todos los beneficios de optimización y comprobación del tipo de GHC.

No me sorprendería si alguien hubiera implementado esto ya. Debería ser trivial.


El problema con la solución aceptada es que el compilador no le dirá cuándo falta una enumeración en su tabla. La solución deriving Enum es excelente, pero no funcionará si desea tener un mapeo arbitrario de los números. Otra respuesta sugiere Generics o Template Haskell. Esto se sigue al usar 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

toNumber advertencia del compilador en el toNumber caso toNumber cuando se agregue un nuevo constructor.

Ahora solo necesitamos la capacidad de convertir ese código en datos para que la asignación se pueda revertir automáticamente. Aquí generamos la misma table mencionada en la solución aceptada.

table = map (/cData -> let c = (fromConstr cData :: MyDataType) in (c, toNumber c) ) $ dataTypeConstrs $ dataTypeOf Foo

Puede completar una clase Enum la misma manera que en la respuesta aceptada. No mencionado, hay que también puedes completar la clase Bounded .


data MyDataType = Foo | Bar | Baz deriving (Enum)


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