简体   繁体   中英

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?

From what I understand, you're given a list of pixels and you want to change the first component (ie 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.

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM