假设我有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
类型类的实例(它将是通过findMin
和deleteMin
函数轻松表达。
当我们处理需要类型*
的类型类时,可以很容易地表达这种关系,例如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
类型参数设置约束?
答案 0 :(得分:17)
通常,不是,当类型构造函数本身被赋予实例时,就无法约束它所应用的类型。大多数情况下,这是一件好事,因为它可以确保例如Functor
个实例对它们的元素类型是真正不可知的,这有助于保持良好和可预测的行为良好且可预测。
有时候这是一个烦恼,最常见的例子确实需要一个Ord
约束的排序数据结构,否则它可能是一个漂亮,表现良好的实例。
有些实验技术涉及约束类型之类的东西,但在您的具体情况下,已经有了可行的解决方案。如果你看一下Foldable
的定义,它说只需要实现foldMap
或foldr
,所以我们会考虑这些。请注意类型:
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
类型类是否符合您的要求。