Madderote
Madderote

Reputation: 1137

Haskell type classes and instances

Why does the code below require a constraint and the type parameter for an instance of Show but are they need required for making Quad an instance of Functor?

data Quad a = Quad a a a a 
instance (Show a) => Show (Quad a) where
  show (Quad a b c d) = show a ++ " " ++ show b ++ "\n" ++ 
                        show c ++ " " ++ show d
instance Functor Quad where 
  fmap f (Quad a b c d) = Quad (f a) (f b) (f c) (f d) 

Upvotes: 0

Views: 86

Answers (2)

chepner
chepner

Reputation: 530793

Your definition of Show requires show be applied to each value wrapped by the Quad data constructor, which imposes the constraint. It would not be needed if you had a trivial instance like

instance Show (Quad a) where
    show (Quad a b c d) = "some Quad value"

because this definition doesn't care about the type of a et al.:

> show (Quad 1 2 3 4)
"some Quad value"
> show (Quad (+1) (+2) (+3) (+4))
"some Quad value"

fmap, on the other hand, has type (a -> b) -> f a -> f b, because fmap itself places no constraint on the type used by Quad; any such constraints are imposed by whatever function is passed to fmap as its first argument:

> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
> :t fmap show
fmap show :: (Functor f, Show a) => f a -> f String

Sometimes, an instance for Functor will require a constraint. For example, consider the Compose type from Data.Functor.Compose:

data Compare f g = Compose { getCompose :: f (g a) }

Ignoring its name, all it requires is two type constructors with kind Type -> Type. But, if you want a Functor instance for Compose, then those type constructors must also have Functor instances, because we'll use fmap internally.

instance (Functor f, Functor g) => Functor (Compose f g) where
    -- x :: f (g a)
    fmap f (Compose x) = Compose (fmap (fmap f) x)

For example, fmap (+1) [1,2,3] == [2,3,4], but fmap (+1) [[1,2,3], [4,5,6]] wouldn't typecheck, because (+1) can't take a list as an argument. Compose lets us "dig into" the nested functor.

-- Compose [[1,2,3],[4,5,6]] :: Num a => Compose [] [] a
> fmap (+1) (Compose [[1,2,3], [4,5,6]])
Compose [[2,3,4],[5,6,7]]

-- Compose [Just 3, Just 4, Nothing] :: Num a => Compose [] Maybe a
> fmap (+1) (Compose [Just 3, Just 4, Nothing])
Compose [Just 4,Just 5,Nothing]

-- Compose Nothing :: Compose Maybe g a
> fmap (+1) (Compose Nothing)
Nothing

-- Compose (Just [1,2,3]) :: Num a => Compose Maybe [] a
> fmap (+1) (Compose (Just [1,2,3]))
Compose (Just [2,3,4])

Upvotes: 3

Lianne
Lianne

Reputation: 110

You are calling show on the type "inside" Quad so it needs to be an instance of Show but you aren't calling fmap on the values "inside" Quad in your definition of fmap on Quad so there is no reason to require it is an instance of Functor.

Upvotes: 0

Related Questions