有这种类型:
{-# LANGUAGE GADTs #-}
data Rgb a = (Num a, Show a) => Rgb a a a
我完全能够实现 Show
类型类:
instance Show (Rgb a) where
show (Rgb r g b) = "Rgb (" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"
但是如果我尝试对Functor
做同样的事情:
instance Functor (Rgb a) where
fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
我在 GHCi REPL 上得到以下输出:
<interactive>;:1093:19:
The first argument of ‘Functor’ should have kind ‘* > *’,
but ‘Rgb a’ has kind ‘*’
In the instance declaration for ‘Functor (Rgb a)’
我当然会对解决方案和解释感到满意,而且还会有一个加深与这个问题相关的理论的链接。
为了解决这个问题,我(暂时)编写了这个函数:
mapRgb :: (Num a, Num b, Show a, Show b) => (a -> b) -> Rgb a -> Rgb b
mapRgb f (Rgb r g b) = Rgb (f r) (f g) (f b)
但我真的更喜欢为 Rgb
类型实现 fmap
。
最佳答案
您的 Functor
实例不应具有类型参数:
instance Functor Rgb where
fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
如果您想派生实例,包括 Functor
,请使用 DeriveFunctor
编译指示:
{-# LANGUAGE DeriveFunctor #-}
data Rgb a = Rgb a a a -- NOTE: DO NOT CONSTRAIN DATA!
deriving (Show, Eq, Ord, Functor)
此外,数据类型声明的类型约束几乎总是无用的。约束需要这些约束的函数。
<小时/>您发现的问题是由于类型的类型造成的:种类。我们用 *
编写种类,GHCi 中的 :kind
命令可以提供帮助:
λ> :kind Int
Int :: *
λ> :kind Char
Char :: *
λ> :kind Maybe Int
Maybe Int :: *
所有仿函数
都采用类型参数,因此它们都看起来像这样:
λ> :kind Maybe
Maybe :: * -> *
λ> :kind IO
IO :: * -> *
RGB
属于 * -> *
类型,但是当您编写 RGB a
时,您会应用 a::*
到它,它就变成了 RGB a::*
,这对编译器来说没有意义。
现在您应该明白了:
The first argument of ‘Functor’ should have kind ‘* > *’,
but ‘Rgb a’ has kind ‘*’
<小时/>
之前当您尝试实现仿函数实例时失败的原因是由于数据类型的这些限制:
-- Do not do this. This is poor Haskell.
data Rgb a = (Num a, Show a) => Rgb a a a
你应该写:
data Rgb a = Rgb a a a
然后在每个实例上添加约束:
instance (Show a) => Show (RGB a) where
...
instance (Num a) => Num (RGB a) where
...
然后你的仿函数实例就可以了。
关于haskell - 为参数类型实现 Functor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32681585/