我试图定义数据类型Set但是当我尝试实例化仿函数类时我遇到了问题。
这是我的代码:
data Set a where
Empty :: Eq a => Set a
Add :: Eq a => a -> Set a -> Set a
instance Functor Set where
fmap = setMap
setMap :: (Eq a, Eq b) => (a->b) -> Set a -> Set b
setMap f Empty = Empty
setMap f (Add x set) = Add (f x) $ setMap f set
这就是编译器所说的:
No instance for (Eq a) arising from a use of ‘setMap’
Possible fix:
add (Eq a) to the context of
the type signature for:
fmap :: (a -> b) -> Set a -> Set b
为什么setMap定义中的约束不够?或者,如果是,为什么这是错的?
答案 0 :(得分:3)
仿函数是一对函数:将一种类型映射到另一种类型的类型构造函数f
,以及将类型fmap
的函数映射到类型{{a -> b
的函数的函数f a -> f b
1}}。对a
和b
的内容有无限制; f
和fmap
都需要适用于所有类型。
Set
有资格作为类型构造函数;给定任何类型a
,它返回一个新类型Set a
。
setMap
不能与任何两种类型a
和b
一起使用;它仅适用于Eq a => a
和Eq b => b
类型。
所以,Set
/ setMap
对几乎是,但不完全是 Hask 到 Hask的仿函数(或 Hask 上的endofunctor),这是Functor
类型类所代表的内容。 ( Hask 是有争议的类别,定义为所有Haskell类型的类以及在这些类型上定义的函数。)
但是,可以定义 Hask 的子类别 HaskEq ,其对象将是具有Eq
实例的所有Haskell类型。然后Set
/ setMap
将成为 HaskEq 的endofunctor,因为setMap
对 HaskEq 中的任何类型都有效。在Haskell中代表 HaskEq 并不容易,但 Hask 很简单,如Control.Category
所示:
class Category a where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
-- Hask
instance Category (->) where
id = GHC.Base.id
(.) = (GHC.Base..)
但是你可以定义自己的类型类来表示这样的endofunctors:
class EqFunctor f where
eqfmap :: (Eq a, Eq b) => (a -> b) -> f a -> f b
instance EqFunctor Set where
eqfmap = setMap