生成与枚举类型关联的Ints列表

时间:2012-07-29 15:20:24

标签: haskell types

我有一个实用程序函数,它枚举了一个可枚举和有界的类型的所有值:

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 -> Intvals类型为[Int])的原因。

如何编写此代码以使其完成我想要的操作?

1 个答案:

答案 0 :(得分:6)

类型变量的问题在于它们仅绑定在类型签名中。在定义中使用类型变量将引用新的,新的类型变量(即使它与类型签名中的名称完全相同)。

有两种方法可以从签名中引用类型变量:ScopedTypeVariables扩展名和asTypeOf

使用ScopedTypeVariables明确绑定forall的类型变量也可用于定义,因此:

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])

另一种方式涉及函数asTypeOf定义为:

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

如果我们可以在第二个参数中获得[b]类型的表达式,则统一将确保第一个参数也具有类型[b]。因为我们有f :: a -> bf undefined :: b,所以我们可以写:

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])