У меня есть служебная функция, которая перечисляет все значения перечислимого и ограниченного типа :
enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound.. maxBound]
. и тип данных, который включает отображение перечислимых типов в целые числа :
data Attribute a = Attribute { test :: a -> Int
, vals :: [Int]
, name :: String }
. Где vals
— список целых чисел, представляющих все возможные перечисляемые значения. Например, если бы у меня было
data Foo = Zero | One | Two deriving (Enum,Bounded)
тогда vals
будет [0,1,2]
.
Я хочу иметь возможность создавать эти атрибуты программно, просто учитывая функцию, которая отображает a
в перечисляемый тип и имя. Примерно так:
attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum. f) vs str
where
vs = map fromEnum enumerate
Это не проверка типов, потому что нет способа связать вызов enumerate
с b
в сигнатуре типа. Так что я подумал, что смогу это сделать:
vs = map fromEnum $ enumerate :: [b]
но это тоже не компилируется -компилятор переименовывает это b
в b1
. Я пытался быть умнее, используя расширение GADTs:
attribute :: (Enum b, Bounded b, b ~ c) => {-... -}
vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c]
но снова c
переименовывается в c1
.
Я не хочу включать тип b
в качестве параметра в тип Attribute
(, главным образом потому, что хочу хранить списки атрибутов с потенциально разными значениями b
-. вот почему test
имеет тип a -> Int
, а vals
имеет тип [Int]
)..
Как я могу написать этот код, чтобы он делал то, что я хочу?