Why does parametrized type instance works without specifying type parameter

instance Functor A where instead of instance Functor (A a) where. Why does it work ?

I find this easier to understand using GHC's kinding system. Let's start from a simple case, and experiment in GHCi:

> :k Eq Int
Eq Int :: Constraint

This tells us that Eq Int is a constraint, some property that might be verified during type checking. Indeed, if we type check (12 :: Int) == (42 :: Int), the compiler will verify that integers can be compared, resolving the constraint Eq Int.

What is Eq alone, the name of the class without the Int parameter?

> :k Eq
Eq :: * -> Constraint

This tells us that Eq can be thought of a function from types (* is the kind of types) to constraint.

Indeed, in Eq Int, Int is a type, so we have Int :: * making Int a well-kinded argument to pass to Eq.

Enough of type classes, what about type constructors?

> :k Maybe Int
Maybe Int :: *

No surprise, Maybe Int is a type

> :k Maybe
Maybe :: * -> *

Maybe instead, is a function from types to types (*->*). This is indeed what the Maybe type constructor does: mapping a type (Int) to a type (Maybe Int).

Back to the original question. Why can't we write instance Functor (A a) but we can instead write instance Functor A? Well, we have that

> :k A Int
A Int :: *
> :k A
A :: * -> *

and, most importantly,

> :k Functor
Functor :: (* -> *) -> Constraint

This tells us the the kind of the Functor type class is not the same kind of the Eq type class. Eq expects a type as an argument, while Functor expects something of kind (* -> *) as an argument. A fits that kind, while A Int does not.

This happens when, in the definition of the class, the argument is applied to some other type. E.g.

class C1 a where
   foo :: a -> Bool

results in C1 :: * -> Constraint. Instead,

class C2 f where
   bar :: f Int -> Bool

results in C2 :: (* -> *) -> Constraint, since f is not itself used as a type, f Int is, so f must be a parameterized type of kind * -> *.


When should you neglect the type parameter?

The type parameter is not "neglected". Let us first take a look at the Functor type class:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

Notice the f a and f b in the type signature. We thus here "construct types" for the type signature of fmap.

Here f is thus basically a function that takes a type and transforms it into a type. For example if f ~ A if a ~ Int, then f a generates the A Int type.

Learn You a Haskell for the Greater Good! actually explains this in its chapter about Functors, Applicatives and Monoids:

Many times, we want to make our types instances of certain type classes, but the type parameters just don't match up for what we want to do. It's easy to make Maybe an instance of Functor, because the Functor type class is defined like this:

class Functor f where  
    fmap :: (a -> b) -> f a -> f b 

So we just start out with:

  instance Functor Maybe where

And then implement fmap. All the type parameters add up because the Maybe takes the place of f in the definition of the Functor type class and so if we look at fmap like it only worked on Maybe, it ends up behaving like:

fmap :: (a -> b) -> Maybe a -> Maybe b

(...)