In the Foldable
documentaion, I see the following instance:
(Foldable f, Foldable g) => Foldable (Compose * * f g)
If I look at the definition of Compose, I see it is declared as
newtype Compose f g a = Compose f (g a)
which has kind (* -> *) -> (* -> *) -> * -> *
, right? (which I suppose it is inferred). Now as far as I can see, I guess it would be correct to write:
(Foldable f, Foldable g) => Foldable (Compose f g)
Since Compose f g
has also kind * -> *
. However, I guess the two kind
identifiers are there for a reason. But what is this reason?
Technically the kind of Compose
is more general if you turn on PolyKinds
:
newtype Compose (f :: k -> *) (g :: k' -> k) (a :: k') = Compose (f (g a))
This type is inferred with PolyKinds
and requires it to write (and it is defined as such in the library - note that PolyKinds
is enabled in the module).
Technically, this
Compose
takes two kind parameters, which are implicit. This means you do not normally specify them, nor can you normally see them. However, it is a peculiarity of the Haddocks that they do display kind parameters. But if you are interested, you can see the same thing in GHCi, with e.g.
>:set -fprint-explicit-kinds
>import Data.Functor.Compose
>:i Compose
type role Compose nominal nominal representational nominal nominal
newtype Compose k k1 (f :: k -> *) (g :: k1 -> k) (a :: k1)
= Compose {getCompose :: f (g a)}
-- Defined in `Data.Functor.Compose'
....
instance [safe] (Foldable f, Foldable g) =>
Foldable (Compose * * f g)
Note that Compose
is given two kind arguments - *
and *
- because f
and g
must be of kind * -> *
due to the Functor
constraints.