Documentation

GaudisCrypt.Language.ModuleExpression

Module expressions #

Everything about ModuleExpression.reduce that rests on confluence (ModuleExpressionConfluence.lean) or on normalization (ModuleExpressionNormalization.lean): reduce is idempotent, agrees on convertible terms, computes a normal form of a well-typed term, and is a congruence for every constructor — plus the reduce_simp tactic built out of those lemmas.

ModuleExpressionBasics.lean holds the part of the development that needs neither: the syntax tree, typing, substitution, reduction, normal forms and the moduletyping/normalmodule tactics. This file is the one downstream modules import.

Termination is invariant along equireducibility: each ReductionStep preserves it via multiStepReduction_terminating, and refl/symm/trans closure is immediate.

Reduction preserves typing.

The reduct of a well-typed closed term is closed-normal.

The reduct of a well-typed closed term is closed-normal.

@[simp]

A normal term is its own reduct (normal terms are stuck).

Remaining reduce specification lemmas #

theorem GaudisCrypt.ModuleExpression.reduce_pair_cong {a a' b b' : ModuleExpression} (ha : a.reduce = a') (hb : b.reduce = b') :
(a.pair b).reduce = (a'.pair b').reduce

Congruence form for .pair: combines two independently-computed simplifications reduce a = a' / reduce b = b' (however they were obtained — a'/b' need not themselves be fully reduced) into one for the surrounding .pair. Proved directly via convertible: pairL/pairR lift convertible_reduce through .pair on each side, and reduce_convertible_iff turns the resulting convertible witness back into a reduce equation.

theorem GaudisCrypt.ModuleExpression.reduce_app_cong {a a' b b' : ModuleExpression} (ha : a.reduce = a') (hb : b.reduce = b') :
(a.app b).reduce = (a'.app b').reduce

Congruence form for .app, matching reduce_pair_cong.

One-sided congruences. reduce_pair_cong/reduce_app_cong replace both components, so simplifying only one of them means proving reduce b = b' for the other by rfl, i.e. putting reduce b there — a reduce around a component that had none. These leave the other component exactly as it is. (The hypothesis reduce a = a' already forces a' to be reduce a, hence reduce a' = a' by idempotence, which is what makes the untouched side go through.)

Stripping an inner reduce #

reduce only cares about its argument up to convertibility, so a subterm that is already a reduce can be replaced by what it reduces (reduce_idempotent on both sides of the matching _cong lemma). This is what makes the .reduces that toModule leaves behind disappear when a composite expression is reduced — the moduleApply tactic (GaudisCrypt/Language/Syntax2.lean) uses all six of them to bring the two sides of an X.apply_simp goal into the same shape.

Tactics #

reduce_simp #

reduceSimpHead takes a goal of the form reduce M = reduce ?m or reduce M = ?m and solves it by instantiating the rhs as reduce M' where M' is the result of applying a single reduction step.

Rules of the form reduce X = reduce Y or reduce X = Y or convertible X Y or MultiStepReduction X Y or Step X Y in the context are also used as reduction steps.

It fails when no step applies. In particular a term that is already normal is not its business: it never uses reduce_of_normal, so it neither succeeds without reducing nor leaves a reduce around a component it did not touch. Collapsing the reduce of a normal term is the second rule of reduceSimpProcImpl instead.

A plain TacticM function, not a syntax/macro_rules pair, so it adds nothing to the tactic grammar. The head-step alternatives stay a first | … quotation run via evalTactic; the congruence rules — the only recursive part — are tried in order by hand, each followed by a recursive call, with backtracking between them. Invoke it from a proof with run_tac.

Fully normalize reduce m subterms — one reduction step at a time via reduceSimpHead, then the reduce collapsed via reduce_of_normal (see reduceSimpProcImpl) — repeatedly, including inside freshly-exposed nested reduces (handled by simp's own subterm traversal, not by this tactic). Lenient per subterm — whatever it can't reduce is left as it is — but, like simp, it fails when that is every subterm and the goal comes back unchanged. Closes the goal outright if it simplifies all the way to True.

substitute and friends are in the simp set because reduce_beta's result is reduce (body.substitute arg), with substitute a structurally recursive def that no reduceSimpHead branch matches on: unfolding it is what turns that result back into a constructor tree the next step can work on.

The tactic keyword reduce_simp is this function; it stays a keyword because proofs are written with it by hand. Tactics that want it as a step call reduceSimp directly rather than going back through the grammar.

Equations
  • One or more equations did not get rendered due to their size.
Instances For

    Fully normalize reduce m subterms — one reduction step at a time via reduceSimpHead, then the reduce collapsed via reduce_of_normal (see reduceSimpProcImpl) — repeatedly, including inside freshly-exposed nested reduces (handled by simp's own subterm traversal, not by this tactic). Lenient per subterm — whatever it can't reduce is left as it is — but, like simp, it fails when that is every subterm and the goal comes back unchanged. Closes the goal outright if it simplifies all the way to True.

    substitute and friends are in the simp set because reduce_beta's result is reduce (body.substitute arg), with substitute a structurally recursive def that no reduceSimpHead branch matches on: unfolding it is what turns that result back into a constructor tree the next step can work on.

    The tactic keyword reduce_simp is this function; it stays a keyword because proofs are written with it by hand. Tactics that want it as a step call reduceSimp directly rather than going back through the grammar.

    Equations
    Instances For