Parametric polymorphism is the parameterization of a term by a type, permitting the term to be used in differently-typed contexts. Types of polymorphic terms contain universal quantification. For the sake of inference and ease of type checking, mainstream functional programming languages restrict where in the type the quantifiers may appear and what they quantify over. The Hindley-Milner system, or core ML, limits polymorphism to rank-1 (prenex): all quantifiers in a type must be at the front and quantify over monomorphic types. OCaml and Haskell relax the rank restriction, permitting quantifiers inside certain types such as records. Haskell also permits parameterization by arbitrary type constructors: we can define a tree data type abstracting not only over the type of leaf values but also over the type of the collection to hold node's children.

From time to time we run into the limits of polymorphism in Haskell, let alone in ML. We may need to parameterize by arbitrary type functions rather than mere constructors; we may need to quantify over not only types but also kinds. Or we think we need. Some of these demanding problems turn out solvable even in ML. On this page we collect such examples. Most of the code is simple OCaml, demonstrating that the plain Hindley-Milner system can be surprisingly expressive.