为参数类型实现 Functor
Implementing Functor for a parametric type
有这种类型:
{-# 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)’
我当然会对解决方案和解释感到满意,但也会link深化与这个问题相关的理论。
为了克服这个问题,我(暂时)编写了这个函数:
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
pragma:
{-# 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 :: *
所有 Functor
都采用类型参数,因此它们看起来都像这样:
λ> :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
...
然后你的仿函数实例就没问题了。
有这种类型:
{-# 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)’
我当然会对解决方案和解释感到满意,但也会link深化与这个问题相关的理论。
为了克服这个问题,我(暂时)编写了这个函数:
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
pragma:
{-# 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 :: *
所有 Functor
都采用类型参数,因此它们看起来都像这样:
λ> :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
...
然后你的仿函数实例就没问题了。