Having this type:
{-# LANGUAGE GADTs #-}
data Rgb a = (Num a, Show a) => Rgb a a a
I'm perfectly able to implement Show
typeclass:
instance Show (Rgb a) where
show (Rgb r g b) = "Rgb (" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"
But if I try to do the same with Functor
:
instance Functor (Rgb a) where
fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
I got the following output on 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)’
I will certainly be happy with solution and explanation, but also a link to deepen theory tied to this question.
To overcome this problem I've (temporarly) wrote this function:
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)
But I really prefer have fmap
implemented for Rgb
type.
Your Functor
instance shouldn't have a type argument:
instance Functor Rgb where
fmap f (Rgb r g b) = Rgb (f r) (f g) (f b)
If you want to derive instances, including Functor
, use the DeriveFunctor
pragma:
{-# LANGUAGE DeriveFunctor #-}
data Rgb a = Rgb a a a -- NOTE: DO NOT CONSTRAIN DATA!
deriving (Show, Eq, Ord, Functor)
Also, type constraints on a datatype declarations are almost always useless. Constrain the functions that need those constraints.
The problem you've discovered is due to the type of types: kinds. We write kinds with *
, and the :kind
command in GHCi can help:
λ> :kind Int
Int :: *
λ> :kind Char
Char :: *
λ> :kind Maybe Int
Maybe Int :: *
All Functor
s take a type argument, so they all look like this:
λ> :kind Maybe
Maybe :: * -> *
λ> :kind IO
IO :: * -> *
RGB
is of kind * -> *
, but when you write RGB a
, you apply a :: *
to it, it becomes RGB a :: *
, which doesn't make sense to the compiler.
This should now make sense to you:
The first argument of ‘Functor’ should have kind ‘* > *’,
but ‘Rgb a’ has kind ‘*’
The reason it failed before when you tried to implement the functor instance is because of these constraints on your datatype:
-- Do not do this. This is poor Haskell.
data Rgb a = (Num a, Show a) => Rgb a a a
You should have written:
data Rgb a = Rgb a a a
And then added constraints on each instance:
instance (Show a) => Show (RGB a) where
...
instance (Num a) => Num (RGB a) where
...
And then your functor instance would have been fine.