Create a list of ints associated with the type Enum

I have a utility function that lists all the values โ€‹โ€‹of a type that is both enumerated and limited:

enumerate :: (Enum a, Bounded a) => [a] enumerate = [minBound .. maxBound] 

and a data type that includes mapping enumerated types to integers:

 data Attribute a = Attribute { test :: a -> Int , vals :: [Int] , name :: String } 

Where vals is a list of integers representing all possible enumerated values. For example, if I had

 data Foo = Zero | One | Two deriving (Enum,Bounded) 

then vals will be [0,1,2] .

I want to be able to create these attributes programmatically, just a given function that maps a to an enumerated type and name. Something like that:

 attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a attribute f str = Attribute (fromEnum . f) vs str where vs = map fromEnum enumerate 

This does not check the type, because there is no way to connect the call with enumerate with b in the type signature. So I thought I could do this:

 vs = map fromEnum $ enumerate :: [b] 

but this also does not compile - the compiler renames b to b1 . I tried to be smarter using the GADT extension:

 attribute :: (Enum b, Bounded b, b ~ c) => {- ... -} vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c] 

but again c renamed to c1 .

I donโ€™t want to include type b as a parameter in the Attribute type (mainly because I want to store attribute lists with potentially different values โ€‹โ€‹of b - thatโ€™s why test is of type a -> Int and vals is of type [Int] ).

How can I write this code to execute what I want?

+6
source share
1 answer

The problem with type variables is that they are only associated with the type signature. Any use of type variables within a definition will refer to a new variable of type new (even if it has the same name as the type signature).

There are two ways to refer to type variables from a signature: ScopedTypeVariables extension and asTypeOf .

With ScopedTypeVariables a type variable explicitly associated with forall is also available in the definition, thus:

 attribute :: forall a b. (Enum b, Bounded b) => (a -> b) -> String -> Attribute a attribute f str = Attribute (fromEnum . f) vs str where vs = map fromEnum (enumerate :: [b]) 

Another method involves the function asTypeOf , defined as:

 asTypeOf :: a -> a -> a asTypeOf = const 

If we can get an expression of type [b] in the second parameter, unification will ensure that the first parameter also has type [b] . Since we have f :: a -> b and f undefined :: b , we can write:

 attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a attribute f str = Attribute (fromEnum . f) vs str where vs = map fromEnum (enumerate `asTypeOf` [f undefined]) 
+6
source

Source: https://habr.com/ru/post/921608/


All Articles