Haskell 类型 类 和实例

Haskell type classes and instances

为什么下面的代码需要 Show 的实例的约束和类型参数,但使 Quad 成为 Functor 的实例是否需要它们?

data Quad a = Quad a a a a 
instance (Show a) => Show (Quad a) where
  show (Quad a b c d) = show a ++ " " ++ show b ++ "\n" ++ 
                        show c ++ " " ++ show d
instance Functor Quad where 
  fmap f (Quad a b c d) = Quad (f a) (f b) (f c) (f d) 

您在类型 "inside" Quad 上调用 show,因此它需要是 Show 的一个实例,但您没有在值 fmap 上调用 "inside" Quad 在 Quad 上的 fmap 定义中,因此没有理由要求它是 Functor 的实例。

您对 Show 的定义要求 show 应用于每个由 Quad 数据构造函数包装的值,这会施加约束。如果你有一个像

这样的小例子,就不需要了
instance Show (Quad a) where
    show (Quad a b c d) = "some Quad value"

因为此定义不关心 a 等人的类型:

> show (Quad 1 2 3 4)
"some Quad value"
> show (Quad (+1) (+2) (+3) (+4))
"some Quad value"

另一方面,

fmap 具有类型 (a -> b) -> f a -> f b,因为 fmap 本身对 Quad 使用的类型没有任何限制;任何此类约束都由传递给 fmap 作为其第一个参数的任何函数强加:

> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
> :t fmap show
fmap show :: (Functor f, Show a) => f a -> f String

有时,Functor 的实例将 需要约束。例如,考虑来自 Data.Functor.Compose:

Compose 类型
data Compare f g = Compose { getCompose :: f (g a) }

忽略它的名字,它所需要的只是两个种类为 Type -> Type 的类型构造函数。但是,如果您想要 ComposeFunctor 实例,那么这些类型构造函数也必须有 Functor 个实例,因为我们将在内部使用 fmap

instance (Functor f, Functor g) => Functor (Compose f g) where
    -- x :: f (g a)
    fmap f (Compose x) = Compose (fmap (fmap f) x)

例如,fmap (+1) [1,2,3] == [2,3,4],但 fmap (+1) [[1,2,3], [4,5,6]] 不会进行类型检查,因为 (+1) 不能将列表作为参数。 Compose 让我们 "dig into" 嵌套仿函数。

-- Compose [[1,2,3],[4,5,6]] :: Num a => Compose [] [] a
> fmap (+1) (Compose [[1,2,3], [4,5,6]])
Compose [[2,3,4],[5,6,7]]

-- Compose [Just 3, Just 4, Nothing] :: Num a => Compose [] Maybe a
> fmap (+1) (Compose [Just 3, Just 4, Nothing])
Compose [Just 4,Just 5,Nothing]

-- Compose Nothing :: Compose Maybe g a
> fmap (+1) (Compose Nothing)
Nothing

-- Compose (Just [1,2,3]) :: Num a => Compose Maybe [] a
> fmap (+1) (Compose (Just [1,2,3]))
Compose (Just [2,3,4])