---
title: "A Quickie: A Use Case for Impredicative Polymorphism"
path: impredicative-polymorphism
date: October 19, 2019
---
Amulet now (as of the 18th of October) has support for impredicative
polymorphism based on [Quick Look impredicativity], an algorithm first
proposed for GHC that treats inference of applications as a two-step
process to enable inferring impredicative types.
As a refresher, impredicative types (in Amulet) are types in which a
`forall`{.amuçet} appears under a type constructor (that is not
`(->)`{.amulet} or `(*)`{.amulet}, since those have special variance in
the compiler).
Quick Look impredicativity works by doing type checking of applications
in two phases: the _quick look_, which is called so because it's faster
than regular type inference, and the regular type-checking of
arguments.
Given a `n`-ary application
f x1 ... xn
:
-
The _quick look_ proceeds by inferring the type of the function to
expose the first `n` quantifiers, based on the form of the arguments.
For a regular term argument `e`, we expect a `'t ->`{.amulet} quantifier; For
visible type arguments, we expect either `forall 'a.`{.amulet} or
`forall 'a ->`{.amulet}.
After we have each of the quantifiers, we quickly infer a type for each
of the _simple_ arguments in
x1 ... xn
.
Here, simple means either a
variable, literal, application or an expression annotated with a type
`x : t`{.amulet}. With this type in hands, we unify it with the type
expected by the quantifier, to collect a partial substituion (in which
unification failures are ignored), used to discover impredicative
instantiation.
For example, say `f : 'a -> list 'a -> list 'a`{.amulet} (the cons
function)[^1], and we want to infer the application `f (fun x -> x) (Nil
@(forall 'xx. 'xx -> 'xx))`{.amulet}. Here, the quick look will inspect
each argument in turn, coming up with a `list 'a ~ list (forall 'xx. 'xx
-> 'xx)`{.amulet} equality by looking at the second argument. Since the
first argument is not simple, it tells us nothing. Thus, the second
phase starts with the substitution `'a := forall 'xx. 'xx ->
'xx`{.amulet}.
-
The second phase is traditional type-checking of each argument in turn,
against its respective quantifier. Here we use Amulet's type-checking
function `check` instead of applying type-inference then constraining
with subsumption since that results in more precise resuls.
However, instead of taking the quantifiers directly from the function's
inferred type, we first apply the substitution generated by the
quick-look. Thus, keeping with the example, we check the function `(fun
x -> x)`{.amulet} against the type `forall 'xx. 'xx -> 'xx`{.amulet},
instead of checking it against the type variable `'a`{.amulet}.
This is important because checking against a type variable degrades to
inference + subsumption, which we wanted to avoid in the first place!
Thus, if we had no quick look, the function `(fun x -> x)`{.amulet}
would be given monomorphic type `'t1 -> 't2`{.amulet} (where
`'t1'`{.amulet}, `'t2`{.amulet} are fresh unification variables), and
we'd try to unify `list ('t1 -> 't2) ~ list (forall 'xx. 'xx ->
'xx)`{.amulet} - No dice!
### Why does this matter?
Most papers discussing impredicative polymorphism focus on the boring,
useless example of stuffing a list with identity functions. Indeed, this
is what I demonstrated above.
However, a much more useful example is putting _lenses_ in lists (or
`optional`{.amulet}, `either`{.amulet}, or what have you). Recall the
van Laarhoven encoding of lenses:
```amulet
type lens 's 't 'a 'b <- forall 'f. functor 'f => ('a -> 'f 'b) -> 's -> 'f 't
```
If you're not a fan of that, consider also the profunctor encoding of
lenses:
```amulet
type lens 's 't 'a 'b <- forall 'p. strong 'p => 'p 'a 'b -> 'p 's 't
```
These types are _both_ polymorphic, which means we can't normally have a
`list (lens _ _ _ _)`{.amulet}. This is an issue! The Haskell `lens`
library works around this by providing a `LensLike`{.haskell} type,
which is not polymorphic and takes the functor `f` by means of an
additional parameter. However, consider the difference in denotation
between
```haskell
foo :: [Lens a a Int Int] -> a -> (Int, a)
bar :: Functor f => [LensLike f a a Int Int] -> a -> (Int, a)
```
The first function takes a list of lenses; It can then use these lenses
in any way it pleases. The second, however, takes a list of lens-like
values _that all use the same functor_. Thus, you can't `view` using the
head of the list and `over` using the second element! (Recall that
`view` uses the `Const`{.haskell} functor and `over`{.amulet} the
`Identity`{.amulet} functor). Indeed, the second function can't use the
lenses at all, since it must work for an arbitrary functor and not
`Const`{.haskell}/`Identity`{.haskell}.
Of course, [Amulet lets you put lenses in lists]: See `lens_list` and
`xs` at the bottom of the file.
[^1]: Assume that the `'a`{.amulet} variable is bound by a `forall
'a.`{.amulet} quantifier. Since we don't use visible type application in
the following example, I just skipped mentioning it.
[Quick Look impredicativity]: https://github.com/serras/ghc-proposals/blob/quick-look/proposals/0000-quick-look-impredicativity.md
[Amulet lets you put lenses in lists]: /static/profunctor-impredicative.ml.html