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
的类型构造函数。但是,如果您想要 Compose
的 Functor
实例,那么这些类型构造函数也必须有 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])
为什么下面的代码需要 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
的类型构造函数。但是,如果您想要 Compose
的 Functor
实例,那么这些类型构造函数也必须有 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])