Type Invariants

Type invariants carry a lot of the weight of proofs in practice. Type invariants are sometimes call refinement types. A Refinement Type describes a subset of the values of a Total Type.

A Total Type describes all values that can be represented. UnsignedInteger32 would be a Total Type. UnsignedInteger32 can represent all 32-bit unsigned integers.

By comparison, a refinement type, or implicitly any type with invariants, would not be Total. Odd numbers are a subset of all Integers. Odd numbers might be represented as Integers, but not all Integers are Odd.

987654321 : Odd + Integer

Type Invariants are properties that are attached to a type definition. The definition of Odd is as follows:

type Odd: Integer
   where self%2 | 1

Multiple properties can be attached to the same type definition.

type Factor23: Integer
   where self%2 | 0
   and   self%3 | 0;

The self keyword represents some value of the declared type. The bar syntax indicates that the preceding expression produces the following expression. When a value is bound by a type that has invariants, each invariant is checked as a precondition to satisfy that type. If a bound value does not satisfy its type's preconditions, then an error will occur.

Postconditions are the properties that we know about any given value. We may not know the exact value of a term, however we may know some of its properties. When this happens, we can often use the postconditions of a type to complete a proof.

In the above example of Factor23, we know that any value of that type will divide evenly into 2 and 3. This can be used as part of a proof, or in a program it can be used for constant folding.

let x:Factor23;
if x%2 == 0 then {
   //yes, always this branch
} else {
   //no, never this branch