Подтвердить что ты не робот

Лучший способ определить перечисление в 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 

Обратите внимание, что я должен повторять одну пару два раза - один раз при определении отображения целочисленного числа и другого времени при определении отображения перечисления в целое. Есть ли способ избежать этого повторения?

Спасибо.

4b9b3361

Ответ 1

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

Ответ 2

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

Ответ 3

Проблема с принятым решением - компилятор не скажет вам, когда вам не хватает перечисления в вашей таблице. Решение deriving Enum отлично, но оно не будет работать, если вы хотите иметь произвольное сопоставление с числами. Другой ответ предлагает Generics или Template 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

Мы получим предупреждение компилятора в сопоставлении case toNumber при добавлении нового конструктора.

Теперь нам просто нужна возможность превратить этот код в данные, чтобы можно было автоматически перевернуть отображение. Здесь мы генерируем тот же table, который указан в принятом решении.

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

Вы можете заполнить класс Enum так же, как и в принятом ответе. Не указано, что вы также можете заполнить класс Bounded.

Ответ 4

Поскольку вы говорите, что цифры не генерируются никаким регулярным законом, вы можете использовать общее программирование (например, с помощью Scrap Your Boilerplate) или Template Haskell для реализации общего решения этой проблемы. Я предпочитаю Template Haskell, потому что он действительно генерирует код и компилирует его, поэтому вы получаете все преимущества проверки и оптимизации типов GHC.

Я бы не удивился, если бы кто-то уже реализовал это. Это должно быть тривиально.