我有一个实用函数,它枚举一个类型的所有值,该类型既可枚举又是有界的:
enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound .. maxBound]
以及涉及将可枚举类型映射到整数的数据类型:
data Attribute a = Attribute { test :: a -> Int
, vals :: [Int]
, name :: String }
wherevals
是表示所有可能的可枚举值的整数列表。例如,如果我有
data Foo = Zero | One | Two deriving (Enum,Bounded)
那么vals
将是[0,1,2]
。
我希望能够以编程方式创建这些属性,只需给定一个将 an 映射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
has typea -> Int
和vals
has type [Int]
)。
我怎样才能编写这段代码,以便它完成我想要它做的事情?