I have a utility function that enumerates all values of a type that is both enumerable and bounded:
enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound .. maxBound]
and a data type that involves mapping enumerable types to integers:
data Attribute a = Attribute { test :: a -> Int
, vals :: [Int]
, name :: String }
Where vals is the list of integers representing all possible enumerable values. For example, if I had
data Foo = Zero | One | Two deriving (Enum,Bounded)
then vals would be [0,1,2].
I want to be able to create these attributes programatically, just given a function that maps an a to an enumerable type, and a name. Something like this:
attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum . f) vs str
where
vs = map fromEnum enumerate
This doesn't typecheck, because there's no way of connecting the call to enumerate with the b in the type signature. So I thought I could do this:
vs = map fromEnum $ enumerate :: [b]
but that doesn't compile either - the compiler renames that b to b1. I tried to be smarter, using the GADTs extension:
attribute :: (Enum b, Bounded b, b ~ c) => {- ... -}
vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c]
but again, the c is renamed to c1.
I don't want to include the type of b as a parameter in the Attribute type (mainly because I want to store lists of attributes with potentially different values of b - that's why test has type a -> Int and vals has type [Int]).
How can I write this code so that it does what I want it to do?