Kinds and Higher-Kinded Types

-- Kinds are "types of types"
-- Int :: Type          (Type is also written *)
-- Maybe :: Type -> Type
-- Either :: Type -> Type -> Type
-- Either String :: Type -> Type
-- StateT :: Type -> (Type -> Type) -> Type -> Type
-- Functor :: (Type -> Type) -> Constraint

{-# LANGUAGE KindSignatures #-}
import Data.Kind (Type)

-- Higher-kinded type parameter: f has kind Type -> Type
data Wrapper (f :: Type -> Type) a = Wrapper (f a)

newtype Compose f g a = Compose (f (g a))
instance (Functor f, Functor g) => Functor (Compose f g) where
  fmap h (Compose x) = Compose (fmap (fmap h) x)

-- "Higher-kinded data" pattern: one record definition, different wrappers
data PersonF f = PersonF {pName :: f String, pAge :: f Int}
type Person = PersonF Identity           -- import Data.Functor.Identity
type PartialPerson = PersonF Maybe       -- e.g., form input where fields may be missing

validatePerson :: PartialPerson -> Maybe Person
validatePerson (PersonF n a) = PersonF <$> (Identity <$> n) <*> (Identity <$> a)