RCompOk: the success-restricted Kleisli relation #
RComp (Integrations/ParamTripleTransfer.lean) relates two computations in one
monad by wp-refinement with a shared failure barrel. This module relates a
computation c : M α in a monad whose Std.Do postcondition shape may carry
exception components (a model that fails, for example on arithmetic overflow or an
out-of-range index) to a computation c' : N α' in a monad of shape .pure
(a total model, for example Id):
RCompOk Rα c c' := ∀ Q', (⊢ₛ wp⟦c'⟧ (⇓ a' => ⌜Q' a'⌝)) →
⊢ₛ wp⟦c⟧ (⇓? a => ⌜∃ a', Rα a a' ∧ Q' a'⌝)
Every postcondition that c' establishes is established, up to Rα, by every
successful execution of c; a failing execution of c is unconstrained. At
N = Id this is the statement "if c returns a, then Rα a c'.run"
(RCompOk.id_iff). The relation is stated through wp for M only, so it
applies to any M with a WPMonad instance.
Rules #
RCompOk.pure,RCompOk.bind— the Kleisli rules;bindcomposes;RCompOk.mono— weakening of the value relation;RCompOk.ite,RCompOk.dite— conditionals on both sides, with equivalent conditions;RCompOk.ite_left,RCompOk.dite_left— a conditional on theMside only (a runtime check with no counterpart inN);RCompOk.bind_left— a computation on theMside only, described by a partial-correctness postcondition;RCompOk.of_wp_false— a computation that never succeeds relates to anything;RCompOk.forIn_list,RCompOk.forIn_range,RCompOk.forIn_rco—forloops overList.Forall₂-related lists, over[a:b]and overa...b, with early exit throughRForInStep.
Transfer #
RCompOk.transfer moves a total-correctness fact about c' to a
partial-correctness fact about c: if every result of c' satisfies Q', and
Rα a a' → Q' a' → Q a, then every successful result of c satisfies Q.
RCompOk.triple_transfer is the same statement in Std.Do triple notation. The
converse direction does not hold: a computation that always fails is
RCompOk-related to every c' and satisfies every partial-correctness
postcondition (Examples/RCompOkExamples.lean, transfer_reverse_false).
Containers #
ArrayRel R is the pointwise relation on arrays, List.Forall₂ R on the
underlying lists (the relation R_array of Combinators/ParamArray.lean is
ArrayRel at a Param relation, R_array_eq_arrayRel). ArrayRel.iff_getElem
states it by size and index; ArrayRel.ofFn_iff states agreement of an array with
an indexed family Fin n → α.
The rcomp_ok tactic #
rcomp_ok walks both programs of an RCompOk goal and applies the rule matching
the head of the M side, leaving the value-relation leaves as goals. See
rcompOkCore.
The success-restricted Kleisli lift of a value relation. RCompOk Rα c c'
holds when every postcondition Q' that the total computation c'
establishes is established, up to Rα, by every successful execution of
c: each value a that c returns without an exception has a partner
a' with Rα a a' and Q' a'. The monad N has postcondition shape
.pure, so wp⟦c'⟧ (⇓ a' => ⌜Q' a'⌝) states that every result of c'
satisfies Q'.
Equations
- One or more equations did not get rendered due to their size.
Instances For
bind rule: related heads and pointwise related continuations give related
sequential compositions. The intermediate relation Rα is arbitrary.
Weakening: RCompOk is monotone in the value relation.
A computation with no successful execution (its partial-correctness wp at
the postcondition False holds) is RCompOk-related to every c'.
One-sided bind: a computation c on the M side with no counterpart on the
N side, whose successful results satisfy P, followed by continuations each
related to c'.
ite rule: conditionals with equivalent conditions and branchwise related
arms are related.
dite rule: dependent conditionals with equivalent conditions and branchwise
related arms are related.
One-sided ite: a conditional on the M side whose arms are each related to
c' under the corresponding case hypothesis.
One-sided dite: a dependent conditional on the M side whose arms are each
related to c'.
forIn rule over lists: a loop body mapping related elements and related
accumulators to RForInStep-related steps, folded over List.Forall₂-related
lists from related initial accumulators, gives related loops. A done step
must meet a done step, so early exit is matched.
forIn rule over a range [a:b] (Std.Legacy.Range) iterated on both sides,
with the same index passed to both bodies.
forIn rule over a half-open range a...b (Std.Rco Nat) iterated on both
sides, with the same index passed to both bodies. The range's ForIn
instance places the accumulator in Type.
Transfer from the total side to the successful executions of the partial side.
If every result of c' satisfies Q' and R a a' → Q' a' → Q a, then every
result that c returns without an exception satisfies Q. The converse
direction, from a partial-correctness fact about c to a fact about c',
does not hold, since a c that always fails is related to every c'.
RCompOk.transfer in triple notation: a total-correctness triple for c'
gives a partial-correctness triple for c under any precondition.
At N = Id, RCompOk R c c' states that every successful result of c is
R-related to c'.run.
The pointwise relation on arrays: a and b have the same size and
corresponding entries are R-related, stated as List.Forall₂ R on the
underlying lists.
Equations
- Transfer.Param.ArrayRel R a b = List.Forall₂ R a.toList b.toList
Instances For
Agreement of an array with an indexed family: Array.ofFn f is related to b
exactly when b has size n and position k of b is related to f k.
Axiom audit #
Introduce every leading binder of the main goal with a fresh name.
Structural RCompOk descent. On a goal RCompOk R c c' (after introducing
leading binders and dsimp only, which removes have and β-redexes left by
do-elaboration) it tries, in order:
- each supplied lemma, by
exactand then byapply; - by the head of
c:Bind.bind↦RCompOk.bind, with the intermediate relation taken fromuserRels, then from the relations of the enclosing goals (innermost first, includingRof anRForInStep Rgoal), then equality;ite/dite↦RCompOk.ite/RCompOk.ditewhenc'has the same head andRCompOk.ite_left/RCompOk.dite_leftotherwise;ForIn.forIn↦RCompOk.forIn_range,RCompOk.forIn_rco,RCompOk.forIn_listat equality (same list) or at a relation fromuserRels; - otherwise
RCompOk.pure.
It recurses into every resulting RCompOk goal. Condition equivalences
b ↔ b' are closed by Iff.rfl when possible; an RForInStep R leaf on two
yield (or two done) steps is reduced to R. Value-relation leaves and
goals that match no rule are left to the caller.
rcomp_ok assembles an RCompOk witness by structural descent over both
programs (see rcompOkCore) and leaves the value-relation leaves as goals.
rcomp_ok using R₁, …, Rₙ supplies intermediate relations for bind and
forIn, tried before the relations of the enclosing goals;
rcomp_ok [h₁, …, hₘ] supplies RCompOk lemmas for sub-programs (a called
function, a failing primitive), tried at each node before the structural
rules. It fails on a goal that is not RCompOk _ _ _.
Equations
- One or more equations did not get rendered due to their size.