hdizzle
hdizzle

Reputation: 107

Why is this Functor instance incorrect?

I've written this code:

newtype Pixel a = Pixel (a,a,a) deriving (Show)

instance Functor [Pixel Int] where
    fmap f [] = []
    fmap f [Pixel(a,b,c)] = [Pixel(f a, b, c)]

I want the functor to apply to the first element in the Pixel type, but I keep getting this error:

New.hs:17:18: error:
• Expecting one fewer arguments to ‘[Pixel Int]’
  Expected kind ‘* -> *’, but ‘[Pixel Int]’ has kind ‘*’
• In the first argument of ‘Functor’, namely ‘[Pixel Int]’
  In the instance declaration for ‘Functor [Pixel Int]’

I'm pretty lost on this issue, is there any way to apply a functor on an entire list? Or do I need to set up a functor for an individual Pixel type and then iterate through a list?

Upvotes: 3

Views: 170

Answers (3)

MCH
MCH

Reputation: 2204

Actually, [Pixel Int] already has an instance of Functor because it is a list []. The Functor instance for list [] is defined in GHC base (it uses the definition of map). Now you just need a function that can be applied to each element of that list.

fmap show [(Pixel 0 0 0),(Pixel 1 0 0), (Pixel 0 1 0)]

Functor is generally defined for some container type. It takes a function and applies it to the contents of the container. Then when you call fmap on a container that has an instance of Functor, the compiler will check that the function can be applied to the elements of that container.

If you are still confused about Functors, I recommend this tutorial: Functors, Applicatives, And Monads In Pictures.

Upvotes: 2

ktzr
ktzr

Reputation: 1645

Your syntax is a bit off, fmap applies a function to a data type and you tell it how. To change values for a list of pixels you would need to map (fmap f) over the list. Give this implementation a try.

instance Functor Pixel where
    fmap f (Pixel (a,b,c)) = Pixel (f a, b, c)

Edit this won't work because a,b,c need to be of the same type and functors allow functions of type a->b.

As @AlexisKing commented you should use fmap, but instead write a function like mapPixelFirst :: (a -> a) -> Pixel a -> Pixel a. Then map this function over user list, don't use fmap.

Upvotes: 1

Aadit M Shah
Aadit M Shah

Reputation: 74204

From what I understand, you're given a list of pixels and you want to change the first component (i.e. the red component) of every pixel. Hence, you want the following function:

changeAllPixels :: [Pixel Int] -> [Pixel Int]

Q: How do we change every element of a list? A: We use map:

changeAllPixels = map changeOnePixel

changeOnePixel :: Pixel Int -> Pixel Int

We only want to change the red component. Hence, we have:

changeOnePixel = changeRedComponent doSomething

changeRedComponent :: (a -> a) -> Pixel a -> Pixel a
changeRedComponent f (Pixel (r, g, b)) = Pixel (f r, g, b)

doSomething :: Int -> Int

Now you only need to implement doSomething. For example, if you want to invert the red component then you could implement doSomething as follows:

doSomething x = 255 - x

Note that we didn't make Pixel an instance of Functor. This is because we only want to change the red component and leave the green and blue components alone. We did however use map which is the fmap for lists.

I think the biggest problem you have is that you don't understand functors well. You should probably spend some time getting acquainted with them.

Upvotes: 5

Related Questions