为类型为* - >的类型类的实例键入参数约束*

时间:2012-09-19 15:08:54

标签: haskell typeclass

假设我有Heap a类型,其中Heap是类型* -> *的类型构造函数。堆上的许多基本操作都要求a类型为Ord类型类的实例。

data Heap a = ...

findMin :: Ord a => Heap a -> a
deleteMin :: Ord a => Heap a -> Heap a

只要Heap类型参数是Foldable类型类的实例,我想将a类型声明为Ord类型类的实例(它将是通过findMindeleteMin函数轻松表达。

当我们处理需要类型*的类型类时,可以很容易地表达这种关系,例如Show

instance Show a => Show (Heap a) where
    show h = ...

但我在声明Foldable时遇到问题:

instance Foldable Heap where
    -- Ouch, there is no `a` type parameter to put the constraint on!
    foldr f z h = ...

是否可以在此类实例声明中对a类型参数设置约束?

2 个答案:

答案 0 :(得分:17)

通常,不是,当类型构造函数本身被赋予实例时,就无法约束它所应用的类型。大多数情况下,这是一件好事,因为它可以确保例如Functor个实例对它们的元素类型是真正不可知的,这有助于保持良好和可预测的行为良好且可预测。

有时候这是一个烦恼,最常见的例子确实需要一个Ord约束的排序数据结构,否则它可能是一个漂亮,表现良好的实例。

有些实验技术涉及约束类型之类的东西,但在您的具体情况下,已经有了可行的解决方案。如果你看一下Foldable的定义,它说只需要实现foldMapfoldr,所以我们会考虑这些。请注意类型:

foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
foldr :: (Foldable t) => (a -> b -> b) -> b -> t a -> b

在这两种情况下,具有Foldable实例的类型只出现一次,作为函数的参数。因此,use GADTs可以Ord约束:

data Heap a where
    Heap :: (Ord a) => ...

通过这样做,只要您创建 Ord值,即使是空堆,您也需要Heap个实例;但当你接收一个Heap值时,它上面的模式匹配会将Ord实例带回范围 - 甚至在Foldable实例内!

请注意,这在许多其他情况下没有用处:

fmap :: (Functor f) => (a -> b) -> f a -> f b

我们可以在Ord上获得a个实例,但我们还需要一个b实例,这是不可能的。

return :: (Monad m) => a -> m a

此处我们还需要提供Ord个实例。

答案 1 :(得分:0)

在Hackage上查看keys库。检查其FoldableWithKey类型类是否符合您的要求。