Polarity Annotations

Agda supports explicitly annotating functions arguments and datatype parameters with their polarities, using a modality system, which the positivity checker can then use to infer positivity. This experimental feature has to be enabled by the --polarity option. Here are some sample uses of the different annotations which type-check:

strictly-positive : @++ Set  Set
strictly-positive A = Nat  A

positive : @+ Set  Set
positive A = (A  Nat)  A

negative : @- Set  Set
negative A = A  Nat

mixed : @mixed Set  Set
mixed A = A  A

unused : @unused Set  Set
unused A = Nat

ex : @- Set  Set
ex A = negative (positive A)

ex2 : @mixed Set  Set
ex2 A = mixed (strictly-positive A)

ex3 : @++ Set  Set
ex3 A = unused (negative A)

The following code doesn’t type-check:

ex3 : @++ Set  Set
ex3 A = mixed (strictly-positive A)

The standard use-case is defining the fix-point of any strictly positive type-former (which is already the criterion for an inductive type to be definable in Agda, see Strict positivity, but the polarity modality internalizes this criterion into the type system):

data Mu (F : @++ Set  Set) : Set where
  fix : F (Mu F)  Mu F

In the example above, because F is specified as using its argument strictly positively, the positivity checker allows F (Mu F) as an argument to a constructor since it knows the recursive call to Mu F is in strictly positive position.

When defining functions that take arguments annotated with @++, the typing rules ensure that those arguments may never actually appear to the left of an arrow. They can syntactically appear to the left of an arrow as arguments to functions that don’t use their arguments, such as in the correct example below:

const : @unused Set  Set
const _ = Nat

typechecks : @++ Set  Set
typechecks A = const A  Nat

The polarity modality

Agda implements polarity annotations using a modal system. Here are the different modalities and their meaning:

Notation

Name

Possible use

@++

Strictly positive

Anywhere except in the domain of a pi/function type

@+

Positive

In an even number of nested domains of pi/function types

@-

Negative

In an odd number of nested domains of pi/function types

@mixed

Mixed

Anywhere

@unused

Unused

Nowhere

Strictly positive types are a syntactical condition described for example in section 2.3 of [1]. A very similar system (without strict positivity) was described in [2], but didn’t use the modality formalism.

When Agda type-checks any definition, it ensures that the variables bound by a lambda-abstraction with annotated types satisfy those restrictions. Note that there is no such restriction when checking the codomain of a pi type, so for example (@++ A : Set) (A A) is perfectly valid!

Polarity annotations can only appear on domains of function types and data/record type parameters. Pattern-matching on annotated arguments is only supported for mixed arguments.

Positivity checking

The Agda positivity checker uses the polarity annotations in the typing information to enhance its analysis and accept types like Mu above. This can also help when the positivity checker is unable to automatically infer that information itself. Here is a contrived example that doesn’t type-check without annotations:

apply-pattern-match : {A B : Set₁}  Nat  (@++ A  B)  @++ A  B
apply-pattern-match zero f = f
apply-pattern-match (suc n) f = f

id : {A : Set₁}  @++ A  A
id x = x

data D : Set where
  node : (u : Nat)  apply-pattern-match u id D  D

References

[1] Michael Abbott, Thorsten Altenkirch, Neil Ghani, “Containers: Constructing strictly positive types”, In Theoretical Computer Science, Volume 342, Issue 1, 2005, https://doi.org/10.1016/j.tcs.2005.06.002

[2] Andreas Abel, “Polarized Subtyping for Sized Types”, In: Mathematical Structures in Computer Science, 2006, https://doi.org/10.1007/11753728_39