Universal Quantification (forall)
The forall function expresses that a property holds for all values of a type. It is a verification construct: use it in contracts, ghost code, invariants, assertions, and lemmas, not as runtime control flow.
Syntax
forall<T>(pure func (x : T) : Bool = property)
The argument is a pure func literal that takes a bound variable and returns a boolean expression. The quantifier asserts that this expression is true for every possible value of type T. The function literal is lowered to a verifier quantifier; it is not an ordinary first-class function value.
Basic Usage
Use forall in ghost blocks and contracts to express universal properties:
// Prove that addition is commutative for all natural numbers
persistent actor {
public func check() : async () {
ghost {
assert forall<Nat>(pure func (i : Nat) : Bool =
forall<Nat>(pure func (j : Nat) : Bool =
i + j == j + i));
};
};
}
The verifier proves the quantified assertion in the current proof context. Arithmetic facts like this are usually handled by the backend arithmetic theory; collection and array facts often need guards, triggers, or helper lemmas.
Array Bounds Pattern
The most common use of forall is expressing properties over array indices. Use implication (==>) to guard the property with bounds:
// Prove all array elements are positive
persistent actor {
public func check() : async () {
ghost {
let arr : [Nat] = [1, 2, 3, 4, 5];
let n : Nat = arr.size();
// Guard property with bounds check
assert forall<Nat>(pure func (i : Nat) : Bool =
(i < n) ==> (arr[i] >= 1));
};
};
}
The pattern i < arr.size() ==> property(arr[i]) ensures the property only applies to valid indices. This is the same guard shape you use in loop invariants when proving that each processed element of an array was updated correctly.
Nested Quantifiers
You can nest forall to quantify over multiple variables:
// Prove associativity with three bound variables
persistent actor {
public func check() : async () {
ghost {
assert forall<Nat>(pure func (a : Nat) : Bool =
forall<Nat>(pure func (b : Nat) : Bool =
forall<Nat>(pure func (c : Nat) : Bool =
(a + b) + c == a + (b + c))));
};
};
}
Each nested quantifier introduces a new bound variable with its own scope.
Using forall in Contracts
Quantifiers work in requires and ensures clauses:
// Use forall in preconditions and postconditions
persistent actor {
var data : [var Nat] = [var 1, 2, 3];
// Require all elements positive, ensure they stay positive
public func increment() : async ()
reads data
modifies data
requires forall<Nat>(pure func (i : Nat) : Bool =
i < data.size() ==> data[i] > 0);
ensures forall<Nat>(pure func (i : Nat) : Bool =
i < data.size() ==> data[i] > 0);
{
var idx : Nat = 0;
while (idx < data.size()) {
loop:invariant idx <= data.size();
loop:invariant forall<Nat>(pure func (j : Nat) : Bool =
j < data.size() ==> data[j] > 0);
data[idx] := data[idx] + 1;
idx := idx + 1;
};
};
}
Collections
Use forall with spec collections to express membership properties:
// Quantify over set membership
persistent actor {
public func check() : async () {
ghost {
let s : Set<Nat> = Set.singleton(1);
// All elements in the set are non-negative
assert forall<Nat>(pure func (k : Nat) : Bool =
Set.contains(k, s) ==> k >= 0);
};
};
}
The same pattern works with Map.contains, Set.contains, and sequence index properties from Seq.
Explicit Triggers with forallWith
When automatic trigger inference fails or causes performance issues, use forallWith to provide explicit triggers:
forallWith<T>(
pure func (x : T) : Bool = property,
[pure func (x : T) : Bool = trigger_expression]
)
// Use forallWith for explicit trigger control
persistent actor {
public func check() : async () {
ghost {
let data : [Bool] = [true, true, true];
// Explicit trigger: data[i] guides instantiation
assert forallWith<Nat>(
pure func (i : Nat) : Bool = i < data.size() ==> data[i] == data[i],
[pure func (i : Nat) : Bool = data[i]]
);
};
};
}
Triggers must be "call-like" expressions: function calls, array accesses, field accesses, sequence indexing, or predicate applications. Comparison operators (<, >=, ==) are not valid triggers. See Triggers for the full selection rules.
Domain Constraints
When you quantify over Nat types, the verifier automatically adds x >= 0 constraints. This transformation happens internally:
// You write:
forall<Nat>(pure func (i : Nat) : Bool = property(i))
// Verifier generates:
forall i : Int :: (i >= 0) ==> property(i)
This preserves Sector9's Motoko-derived semantics where Nat values are non-negative.
Common Mistakes
Missing Bounds Guard
- Wrong
- Correct
// WRONG: Missing bounds guard causes verification failure
persistent actor {
public func check() : async () {
ghost {
let arr : [Nat] = [1, 2, 3];
// This fails: tries to prove for ALL Nat, not just valid indices
assert forall<Nat>(pure func (i : Nat) : Bool =
arr[i] >= 1);
};
};
}
// CORRECT: Bounds guard limits property to valid indices
persistent actor {
public func check() : async () {
ghost {
let arr : [Nat] = [1, 2, 3];
// Only prove for indices within bounds
assert forall<Nat>(pure func (i : Nat) : Bool =
i < arr.size() ==> arr[i] >= 1);
};
};
}
Without the bounds guard, the quantifier tries to prove the property for all natural numbers, including indices beyond the array size.
Invalid Trigger Expression
Triggers must be call-like. Comparison expressions are rejected:
// REJECTED: k > 0 is a comparison, not call-like
forallWith<Nat>(
pure func (k : Nat) : Bool = k >= 0,
[pure func (k : Nat) : Bool = k > 0]
)
Use function calls or array accesses as triggers instead.
Performance Considerations
Quantifiers can cause SMT solver timeouts if not used carefully:
- Minimize nesting depth - Deeply nested quantifiers increase solver complexity exponentially
- Use explicit triggers - When automatic inference produces too many triggers, use
forallWith - Keep trigger expressions simple - Triggers guide solver instantiation; complex triggers cause over-instantiation
- Prefer bounded quantification -
i < n ==> property(i)is more efficient than unbounded quantification - Factor reusable facts into lemmas - If several methods repeat the same quantified argument, state it once as a lemma
Related Topics
- Existential quantifiers for proving that a witness exists
- Quantifier patterns for sorted arrays, uniqueness, and
old()comparisons - Performance: triggers for solver tuning in larger proofs
- Logical implication for the guard pattern used in universal quantifiers
Summary
forall<T>(pure func (x : T) : Bool = ...)asserts a property holds for all values- Use implication (
==>) to guard properties with bounds or membership tests - Nest
forallfor multiple bound variables - Use
forallWithwhen you need explicit trigger control - Triggers must be call-like: function calls, array reads, or field accesses
- Domain constraints for
Nattypes are added automatically