Haskell:数据定义和Functor类

时间:2018-04-11 17:51:22

标签: haskell functor

我试图定义数据类型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定义中的约束不够?或者,如果是,为什么这是错的?

1 个答案:

答案 0 :(得分:3)

仿函数是一对函数:将一种类型映射到另一种类型的类型构造函数f,以及将类型fmap的函数映射到类型{{a -> b的函数的函数f a -> f b 1}}。对ab的内容有限制; ffmap都需要适用于所有类型。

Set有资格作为类型构造函数;给定任何类型a,它返回一个新类型Set a

然而,

setMap 不能与任何两种类型ab一起使用;它仅适用于Eq a => aEq 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