为参数类型实现Functor

gss*_*der 2 haskell functional-programming functor

有这种类型:

{-# LANGUAGE GADTs #-}

data Rgb a = (Num a, Show a) => Rgb a a a
Run Code Online (Sandbox Code Playgroud)

我完全能够实现Show类型类:

instance Show (Rgb a) where
  show (Rgb r g b) = "Rgb (" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"
Run Code Online (Sandbox Code Playgroud)

但如果我尝试做同样的事情Functor:

instance Functor (Rgb a) where
  fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
Run Code Online (Sandbox Code Playgroud)

我在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)’
Run Code Online (Sandbox Code Playgroud)

我一定会对解决方案和解释感到满意,但也会加深与此问题相关的深化理论.

为了解决这个问题,我(临时)写了这个函数:

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)
Run Code Online (Sandbox Code Playgroud)

但我真的更喜欢fmap实现Rgb类型.

AJF*_*mar 8

您的Functor实例不应该有类型参数:

instance Functor Rgb where
  fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
Run Code Online (Sandbox Code Playgroud)

如果要派生实例,包括Functor,请使用DeriveFunctorpragma:

{-# LANGUAGE DeriveFunctor #-}

data Rgb a = Rgb a a a                   -- NOTE: DO NOT CONSTRAIN DATA!
    deriving (Show, Eq, Ord, Functor)
Run Code Online (Sandbox Code Playgroud)

此外,对数据类型声明的类型约束几乎总是无用的.约束需要这些约束的函数.


您发现的问题是由于类型的类型:种类.我们写的种类*,:kindGHCi中的命令可以帮助:

?> :kind Int
Int :: *
?> :kind Char
Char :: *
?> :kind Maybe Int
Maybe Int :: *
Run Code Online (Sandbox Code Playgroud)

所有Functors都采用类型参数,所以它们看起来像这样:

?> :kind Maybe
Maybe :: * -> *
?> :kind IO
IO :: * -> *
Run Code Online (Sandbox Code Playgroud)

RGB是善良的* -> *,但是当你写RGB a,你应用a :: *它,它变成RGB a :: *,这对编译器没有意义.

这应该对你有意义:

The first argument of ‘Functor’ should have kind ‘* > *’,
  but ‘Rgb a’ has kind ‘*’
Run Code Online (Sandbox Code Playgroud)

之前尝试实现仿函数实例时失败的原因是因为对数据类型的这些约束:

-- Do not do this. This is poor Haskell.
data Rgb a = (Num a, Show a) => Rgb a a a
Run Code Online (Sandbox Code Playgroud)

你应该写的:

data Rgb a = Rgb a a a
Run Code Online (Sandbox Code Playgroud)

然后在每个实例上添加约束:

instance (Show a) => Show (RGB a) where
    ...

instance (Num a) => Num (RGB a) where
    ...
Run Code Online (Sandbox Code Playgroud)

那么你的算子实例就没问题了.