我有一个实用函数,它枚举一个类型的所有值,该类型既可枚举又是有界的:
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- 这就是为什么testhas typea -> Int和valshas type [Int])。
我怎样才能编写这段代码,以便它完成我想要它做的事情?