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)]