Search code examples
haskellfunctional-programmingtypeclasstype-kinds

The role of the asterisks (kinds, *) in this Foldable instance


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?


Solution

  • 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.