The iterator library provides a large number of useful lemmas.
Most theorems about finite iterators can be proven by rewriting the statement to one about lists, using the fact that the correspondence between iterator combinators and corresponding list operations has already been proved.
In practice, many of these theorems are already registered as simp lemmas.
The lemmas have a very predictable naming system, and many are in the default simp set.
Some of the most important include:
Consumer lemmas such as Iter.all_toList, Iter.any_toList, and Iter.foldl_toList that introduce lists as a model.
Simplification lemmas such as Iter.toList_map that Iter.toList_filter push the list model “inwards” in the goal.
Producer lemmas such as List.toList_iter and Array.toList_iter that replace a producer with a list model, removing iterators from the goal entirely.
The latter two categories are typically automatic with simp.
Reasoning via Lists
Every element returned by an iterator that multiplies the numbers consumed some other iterator by two is even.
To prove this statement, Iter.all_toList, Iter.toList_map, and Array.toList_iter are used to replace the statement about iterators with one about lists, after which simp discharges the goal:
When there are not enough lemmas to prove a property by rewriting to a list model, it can be necessary to prove things about iterators by reasoning directly about their step functions.
The induction principles in this section are useful for stepwise reasoning.
Induction principle for productive iterators: One can define a function f that maps every
iterator it to an element of motiveit by defining fit in terms of the values of f on
the plausible skip successors of `it'.
Induction principle for productive monadic iterators: One can define a function f that maps every
iterator it to an element of motiveit by defining fit in terms of the values of f on
the plausible skip successors of `it'.
Induction principle for finite iterators: One can define a function f that maps every
iterator it to an element of motiveit by defining fit in terms of the values of f on
the plausible successors of `it'.
Induction principle for finite monadic iterators: One can define a function f that maps every
iterator it to an element of motiveit by defining fit in terms of the values of f on
the plausible successors of `it'.
The standard library also includes lemmas for the stepwise behavior of all the producers and combinators.
Examples include List.step_iter_nil, List.step_iter_cons, IterM.step_map.
PostconditionTmα represents an operation in the monad m together with a
intrinsic proof that some postcondition holds for the α valued monadic result.
It consists of a predicate P about α and an element of m({a//Pa}) and is a helpful tool
for intrinsic verification, notably termination proofs, in the context of iterators.
PostconditionTm is a monad if m is. However, note that PostconditionTmα is a structure,
so that the compiler will generate inefficient code from recursive functions returning
PostconditionTmα. Optimizations for ReaderT, StateT etc. aren't applicable for structures.
If m is a monad, then HetTm is a monad that has two features:
It generalizes m to arbitrary universes.
It tracks a postcondition property that holds for the monadic return value, similarly to
PostconditionT.
This monad is noncomputable and is merely a vehicle for more convenient proofs, especially proofs
about the equivalence of iterators, because it avoids universe issues and spares users the work
to handle the postconditions manually.
Caution: Just like PostconditionT, this is not a lawful monad transformer.
To lift from m to HetTm, use HetT.lift.
Because this monad is fundamentally universe-polymorphic, it is recommended for consistency to
always use the methods HetT.pure, HetT.map and HetT.bind instead of the homogeneous versions
Pure.pure, Functor.map and Bind.bind.
The actual monadic operation. Its return value is bundled together with a proof that
it satisfies Property and squashed so that it fits into the monad m.
A noncomputable variant of IterM.step using the HetT monad.
It is used in the definition of the equivalence relations on iterators,
namely IterM.Equiv and Iter.Equiv.
Applies the given function to the result of the contained m-monadic operation with a
proof that the postcondition property holds, returning another operation in m.
Iterator equivalence is defined in terms of the observable behavior of iterators, rather than their implementations.
In particular, the internal state is ignored.
Equivalence relation on iterators. Equivalent iterators behave the same as long as the
internal state of them is not directly inspected.
Two iterators (possibly of different types) are equivalent if they have the same
Iterator.IsPlausibleStep relation and their step functions are the same up to equivalence of the
successor iterators. This coinductive definition captures the idea that the only relevant feature
of an iterator is its step function. Other information retrievable from the iterator -- for example,
whether it is a list iterator or an array iterator -- is totally irrelevant for the equivalence
judgement.
Equivalence relation on monadic iterators. Equivalent iterators behave the same as long as the
internal state of them is not directly inspected.
Two iterators (possibly of different types) are equivalent if they have the same
Iterator.IsPlausibleStep relation and their step functions are the same up to equivalence of the
successor iterators. This coinductive definition captures the idea that the only relevant feature
of an iterator is its step function. Other information retrievable from the iterator -- for example,
whether it is a list iterator or an array iterator -- is totally irrelevant for the equivalence
judgement.