Module expressions #
The raw calculus underlying modules: ModuleTypeRep (the types), the untyped syntax tree
ModuleExpression with extrinsic typing (ModuleExpression.HasType), de Bruijn
renaming/substitution, call-by-value reduction with its normal forms, and the deciding tactics
moduletyping, normalmodule and reduce_simp.
Modules.lean builds Module T — a closed, well-typed, normal expression — on top of this.
Possible types of modules.
- proc : ProcedureSignature → ModuleTypeRep
- prod : ModuleTypeRep → ModuleTypeRep → ModuleTypeRep
- arr : ModuleTypeRep → ModuleTypeRep → ModuleTypeRep
- unit : ModuleTypeRep
Instances For
Instances For
The right-nested product type of a hole signature (native UM copy of the TypedModules definition;
namespace-scoped under GaudisCrypt to avoid clashing with TypedModules's while both coexist).
Equations
Instances For
Untyped module expressions: the raw syntax tree of the module calculus, with no
context/type indices. Well-typedness is captured extrinsically by the predicate
ModuleExpression.HasType. Variables are de Bruijn indices (Nat); abs carries no
domain annotation (the domain is recovered by HasType).
- proc [ProgramSpec] {sig : ProcedureSignature} : Procedure sig → ModuleExpression
- procHoles [ProgramSpec] {holes : HoleSigs} {sig : ProcedureSignature} : holes.NonEmpty → ProcedureWithHoles holes sig → ModuleExpression
- var [ProgramSpec] : ℕ → ModuleExpression
- app [ProgramSpec] : ModuleExpression → ModuleExpression → ModuleExpression
- fst [ProgramSpec] : ModuleExpression → ModuleExpression
- snd [ProgramSpec] : ModuleExpression → ModuleExpression
- abs [ProgramSpec] : ModuleExpression → ModuleExpression
- pair [ProgramSpec] : ModuleExpression → ModuleExpression → ModuleExpression
- unit [ProgramSpec] : ModuleExpression
Instances For
ModuleExpression.HasType m Δ T holds when the untyped expression m is well-typed in
module context Δ with type T. This is the extrinsic counterpart of the indices of
TypedModules.ModuleExpression: every TypedModules.ModuleExpression Δ T erases (via
TypedModules.ModuleExpression.erase) to an m with HasType m Δ T, and conversely.
- proc [ProgramSpec] {Δ : ModuleContext} {sig : ProcedureSignature} (p : Procedure sig) : (ModuleExpression.proc p).HasType Δ (ModuleTypeRep.proc sig)
- procHoles [ProgramSpec] {Δ : ModuleContext} {holes : HoleSigs} {sig : ProcedureSignature} (ne : holes.NonEmpty) (p : ProcedureWithHoles holes sig) : (ModuleExpression.procHoles ne p).HasType Δ (holes.toModuleTypeRepTuple.arr (ModuleTypeRep.proc sig))
- var [ProgramSpec] {Δ : ModuleContext} (n : ℕ) (h : n < List.length Δ) : (ModuleExpression.var n).HasType Δ Δ[n]
- app [ProgramSpec] {Δ : ModuleContext} {A B : ModuleTypeRep} {m n : ModuleExpression} : m.HasType Δ (A.arr B) → n.HasType Δ A → (m.app n).HasType Δ B
- fst [ProgramSpec] {Δ : ModuleContext} {A B : ModuleTypeRep} {m : ModuleExpression} : m.HasType Δ (A.prod B) → m.fst.HasType Δ A
- snd [ProgramSpec] {Δ : ModuleContext} {A B : ModuleTypeRep} {m : ModuleExpression} : m.HasType Δ (A.prod B) → m.snd.HasType Δ B
- abs [ProgramSpec] {Δ : List ModuleTypeRep} {A B : ModuleTypeRep} {m : ModuleExpression} : m.HasType (A :: Δ) B → m.abs.HasType Δ (A.arr B)
- pair [ProgramSpec] {Δ : ModuleContext} {A B : ModuleTypeRep} {m n : ModuleExpression} : m.HasType Δ A → n.HasType Δ B → (m.pair n).HasType Δ (A.prod B)
- unit [ProgramSpec] {Δ : ModuleContext} : ModuleExpression.unit.HasType Δ ModuleTypeRep.unit
Instances For
De Bruijn renaming and substitution on the untyped tree #
The untyped analogues of TypedModules.ModuleExpression.rename/substitute: since there are no type
indices, a renaming is a plain Nat → Nat and a substitution a Nat → ModuleExpression.
These are permanent. Their agreement with the intrinsic versions under erase is recorded by
the transitional erase_rename/erase_substitute* lemmas below.
Lift a de Bruijn renaming under one binder: index 0 is fixed, n+1 ↦ ρ n + 1.
Untyped analogue of liftRenaming.
Equations
- GaudisCrypt.ModuleExpression.liftRen ρ 0 = 0
- GaudisCrypt.ModuleExpression.liftRen ρ n.succ = ρ n + 1
Instances For
Apply a de Bruijn renaming to every variable, going under binders with liftRen.
Untyped analogue of TypedModules.ModuleExpression.rename.
Equations
- GaudisCrypt.ModuleExpression.rename ρ (GaudisCrypt.ModuleExpression.proc p) = GaudisCrypt.ModuleExpression.proc p
- GaudisCrypt.ModuleExpression.rename ρ (GaudisCrypt.ModuleExpression.procHoles ne p) = GaudisCrypt.ModuleExpression.procHoles ne p
- GaudisCrypt.ModuleExpression.rename ρ (GaudisCrypt.ModuleExpression.var n) = GaudisCrypt.ModuleExpression.var (ρ n)
- GaudisCrypt.ModuleExpression.rename ρ (f.app a) = (GaudisCrypt.ModuleExpression.rename ρ f).app (GaudisCrypt.ModuleExpression.rename ρ a)
- GaudisCrypt.ModuleExpression.rename ρ e.fst = (GaudisCrypt.ModuleExpression.rename ρ e).fst
- GaudisCrypt.ModuleExpression.rename ρ e.snd = (GaudisCrypt.ModuleExpression.rename ρ e).snd
- GaudisCrypt.ModuleExpression.rename ρ body.abs = (GaudisCrypt.ModuleExpression.rename (GaudisCrypt.ModuleExpression.liftRen ρ) body).abs
- GaudisCrypt.ModuleExpression.rename ρ (a.pair b) = (GaudisCrypt.ModuleExpression.rename ρ a).pair (GaudisCrypt.ModuleExpression.rename ρ b)
- GaudisCrypt.ModuleExpression.rename ρ GaudisCrypt.ModuleExpression.unit = GaudisCrypt.ModuleExpression.unit
Instances For
Lift a substitution under one binder: index 0 ↦ var 0; n+1 ↦ the weakening of σ n.
Untyped analogue of liftSubstitution.
Equations
Instances For
Apply a simultaneous substitution to every variable, going under binders with liftSubst.
Untyped analogue of substituteSimultaneously.
Equations
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ (GaudisCrypt.ModuleExpression.proc p) = GaudisCrypt.ModuleExpression.proc p
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ (GaudisCrypt.ModuleExpression.procHoles ne p) = GaudisCrypt.ModuleExpression.procHoles ne p
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ (GaudisCrypt.ModuleExpression.var n) = σ n
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ (f.app a) = (GaudisCrypt.ModuleExpression.substituteSimultaneously σ f).app (GaudisCrypt.ModuleExpression.substituteSimultaneously σ a)
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ e.fst = (GaudisCrypt.ModuleExpression.substituteSimultaneously σ e).fst
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ e.snd = (GaudisCrypt.ModuleExpression.substituteSimultaneously σ e).snd
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ body.abs = (GaudisCrypt.ModuleExpression.substituteSimultaneously (GaudisCrypt.ModuleExpression.liftSubst σ) body).abs
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ (a.pair b) = (GaudisCrypt.ModuleExpression.substituteSimultaneously σ a).pair (GaudisCrypt.ModuleExpression.substituteSimultaneously σ b)
- GaudisCrypt.ModuleExpression.substituteSimultaneously σ GaudisCrypt.ModuleExpression.unit = GaudisCrypt.ModuleExpression.unit
Instances For
Single-variable substitution map: 0 ↦ arg, n+1 ↦ var n.
Untyped analogue of variableSubstitution.
Equations
- arg.variableSubstitution 0 = arg
- arg.variableSubstitution n.succ = GaudisCrypt.ModuleExpression.var n
Instances For
Single-variable de Bruijn substitution: replace index 0 in body by arg.
Equations
- body.substitute arg = GaudisCrypt.ModuleExpression.substituteSimultaneously arg.variableSubstitution body
Instances For
Reduction on the untyped tree, and type preservation #
ReductionStep mirrors the intrinsic one but on raw syntax; the payoff is HasType.preservation
— what intrinsic typing gave for free must now be proved. All permanent.
The untyped tuple of procedures corresponding to an instantiation (untyped analogue of
HoleSigs.Instantiation.toModuleTuple).
Equations
- One or more equations did not get rendered due to their size.
- x_2.toModuleExpr = GaudisCrypt.ModuleExpression.unit
Instances For
Non-deterministic single-step reduction on untyped module expressions.
- beta [ProgramSpec] {body arg : ModuleExpression} : (body.abs.app arg).ReductionStep (body.substitute arg)
- appL [ProgramSpec] {f f' arg : ModuleExpression} : f.ReductionStep f' → (f.app arg).ReductionStep (f'.app arg)
- appR [ProgramSpec] {f arg arg' : ModuleExpression} : arg.ReductionStep arg' → (f.app arg).ReductionStep (f.app arg')
- lam [ProgramSpec] {body body' : ModuleExpression} : body.ReductionStep body' → body.abs.ReductionStep body'.abs
- pairL [ProgramSpec] {a a' b : ModuleExpression} : a.ReductionStep a' → (a.pair b).ReductionStep (a'.pair b)
- pairR [ProgramSpec] {a b b' : ModuleExpression} : b.ReductionStep b' → (a.pair b).ReductionStep (a.pair b')
- fstPair [ProgramSpec] {a b : ModuleExpression} : (a.pair b).fst.ReductionStep a
- fst [ProgramSpec] {e e' : ModuleExpression} : e.ReductionStep e' → e.fst.ReductionStep e'.fst
- sndPair [ProgramSpec] {a b : ModuleExpression} : (a.pair b).snd.ReductionStep b
- snd [ProgramSpec] {e e' : ModuleExpression} : e.ReductionStep e' → e.snd.ReductionStep e'.snd
- delta [ProgramSpec] {holes : HoleSigs} {sigs : ProcedureSignature} (ne : holes.NonEmpty) (proc : ProcedureWithHoles holes sigs) (inst : holes.Instantiation) : ((procHoles ne proc).app inst.toModuleExpr).ReductionStep (ModuleExpression.proc (proc.instantiate fun {sig : ProcedureSignature} => inst))
Instances For
A de Bruijn renaming ρ maps context Δ into Γ if every Δ-index has a Γ-index of
the same type sitting at the renamed position.
Equations
- GaudisCrypt.ModuleExpression.HasType.IsRenaming Δ Γ ρ = ∀ {n : ℕ} (h : n < List.length Δ), ∃ (h' : ρ n < List.length Γ), Γ[ρ n] = Δ[n]
Instances For
Weakening: prepending a binder is the renaming Nat.succ.
A context renaming lifts under one binder to liftRen ρ.
Renaming preserves typing along a context renaming.
A substitution σ maps Δ into Γ if it sends every Δ-index to a Γ-typed term.
Equations
- GaudisCrypt.ModuleExpression.HasType.IsSubst Δ Γ σ = ∀ {n : ℕ} (h : n < List.length Δ), (σ n).HasType Γ Δ[n]
Instances For
A well-typed substitution lifts under one binder to liftSubst σ.
Simultaneous substitution preserves typing along a well-typed substitution.
Single-variable substitution preserves typing (the β-substitution lemma).
A term only mentions the variables its context declares, so a substitution that is the
identity on those leaves it alone — whatever it does to the indices beyond Δ. For a closed
term (Δ = []) the hypothesis is vacuous: Module.substituteSimultaneously_expression.
The same for a renaming: a term only mentions the variables its context declares, so a renaming
that is the identity on those leaves it alone. For a closed term (Δ = []) the hypothesis is
vacuous: Module.rename_expression.
Type preservation (subject reduction): reduction preserves the typing judgment. This is
the extrinsic replacement for what the intrinsic TypedModules.ModuleExpression guaranteed by
construction.
Equations
Instances For
Normal forms on the untyped tree #
Untyped analogues of IsProcHoles/IsProcTuple/Normal/Neutral/NormalClosed. Permanent;
mirror the intrinsic versions structurally.
m is a right-nested tuple of hole-free procedures (.unit, or .pair (.proc _) rest
with IsProcTuple rest) — the ground arguments a procedure-with-holes accepts.
Equations
Instances For
Equations
- GaudisCrypt.ModuleExpression.unit.instDecidableIsProcTuple = isTrue trivial
- ((GaudisCrypt.ModuleExpression.proc a_2).pair rest).instDecidableIsProcTuple = match rest.instDecidableIsProcTuple with | isTrue h => isTrue h | isFalse h => isFalse h
- (GaudisCrypt.ModuleExpression.unit.pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- ((GaudisCrypt.ModuleExpression.var a_2).pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- ((a_2.app a_3).pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (a_2.fst.pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (a_2.snd.pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (a_2.abs.pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- ((a_2.pair a_3).pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- ((GaudisCrypt.ModuleExpression.procHoles a_2 a_3).pair rest).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (GaudisCrypt.ModuleExpression.proc a).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (GaudisCrypt.ModuleExpression.var a).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (a.app a_1).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- a.fst.instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- a.snd.instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- a.abs.instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
- (GaudisCrypt.ModuleExpression.procHoles a a_1).instDecidableIsProcTuple = isFalse GaudisCrypt.ModuleExpression.instDecidableIsProcTuple._proof_1
Beta-normal form: no redex anywhere.
- neutral [ProgramSpec] {e : ModuleExpression} : e.Neutral → e.Normal
- abs [ProgramSpec] {body : ModuleExpression} : body.Normal → body.abs.Normal
- pair [ProgramSpec] {a b : ModuleExpression} : a.Normal → b.Normal → (a.pair b).Normal
- proc [ProgramSpec] {sig : ProcedureSignature} {p : Procedure sig} : (ModuleExpression.proc p).Normal
- procHoles [ProgramSpec] {holes : HoleSigs} {sig : ProcedureSignature} {ne : holes.NonEmpty} {p : ProcedureWithHoles holes sig} : (ModuleExpression.procHoles ne p).Normal
- unit [ProgramSpec] : ModuleExpression.unit.Normal
Instances For
Neutral form: no outermost redex — the head is a variable, or a procedure-with-holes applied to a normal non-proc-tuple (so the δ-rule is stuck).
- var [ProgramSpec] {n : ℕ} : (ModuleExpression.var n).Neutral
- app [ProgramSpec] {f arg : ModuleExpression} : f.Neutral → arg.Normal → (f.app arg).Neutral
- appProcHoles [ProgramSpec] {holes : HoleSigs} {sig : ProcedureSignature} {arg : ModuleExpression} {p : ProcedureWithHoles holes sig} (ne : holes.NonEmpty) : arg.Normal → ¬arg.IsProcTuple → ((procHoles ne p).app arg).Neutral
- fst [ProgramSpec] {e : ModuleExpression} : e.Neutral → e.fst.Neutral
- snd [ProgramSpec] {e : ModuleExpression} : e.Neutral → e.snd.Neutral
Instances For
Equations
Equations
Normal form of a closed term. Neutral terms cannot occur closed (they need a free
variable), so there are fewer cases than Normal; the abs body is still general
Normal (it lives under one binder).
- proc [ProgramSpec] {sig : ProcedureSignature} {p : Procedure sig} : (ModuleExpression.proc p).NormalClosed
- procHoles [ProgramSpec] {holes : HoleSigs} {sig : ProcedureSignature} {ne : holes.NonEmpty} {p : ProcedureWithHoles holes sig} : (ModuleExpression.procHoles ne p).NormalClosed
- abs [ProgramSpec] {body : ModuleExpression} : body.Normal → body.abs.NormalClosed
- pair [ProgramSpec] {a b : ModuleExpression} : a.NormalClosed → b.NormalClosed → (a.pair b).NormalClosed
- unit [ProgramSpec] : ModuleExpression.unit.NormalClosed
Instances For
Embedding into Metatheory.STLCext #
Shape predicates for call-by-value reduction (untyped analogues) #
Typing is preserved along multi-step reduction.
Equations
- m.Stuck = ¬∃ (n : GaudisCrypt.ModuleExpression), m.ReductionStep n
Instances For
m terminates: it multi-step reduces to some normal form.
Equations
- m.Terminating = ∃ (n : GaudisCrypt.ModuleExpression), n.Stuck ∧ m.MultiStepReduction n
Instances For
Convertibility: the equivalence relation generated by ReductionStep, i.e. its
reflexive, symmetric, transitive closure.
Equations
Instances For
β-normal form: a chosen normal form reachable from m when it terminates, else unit.
Equations
- m.reduce = if h : m.Terminating then Exists.choose h else Set.Nonempty.some ⋯
Instances For
Multi-step reduction refines convertibility.
Termination is invariant along equireducibility: each ReductionStep preserves it via
multiStepReduction_terminating, and refl/symm/trans closure is immediate.
reduce m is always convertible to m (a reduct if terminating, a class rep otherwise).
Reduction preserves typing.
Progress for well-typed (possibly open) terms: a non-normal well-typed term reduces.
A normal well-typed closed term is closed-normal.
The reduct of a well-typed closed term is closed-normal.
The reduct of a well-typed closed term is closed-normal.
A normal term is its own reduct (normal terms are stuck).
Misc #
A well-typed closed term is never neutral.
app is a congruence for multi-step reduction.
fst is a congruence for multi-step reduction.
pair is a congruence for multi-step reduction.
A pair of stuck expressions is stuck: pair can only step via pairL/pairR.
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.
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.)
Congruence form for .abs, matching reduce_pair_cong/reduce_app_cong.
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 module_apply 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.
A well-typed closed normal term of product type is a pair.
Tactics #
moduletyping #
Syntax-directed closer for ModuleExpression.HasType m Δ T: peels off the HasType
constructor matching m's head, leaves whatever it cannot close (e.g. an unprovable
.var bound) as an open goal. See moduletyping! for a variant that fails loudly
instead.
Equations
- GaudisCrypt.tacticModuletyping = Lean.ParserDescr.node `GaudisCrypt.tacticModuletyping 1024 (Lean.ParserDescr.nonReservedSymbol "moduletyping" false)
Instances For
moduletyping, but fails loudly instead of leaving unclosed goals open. Not fail-fast:
it still runs the core script to completion (closing everything it can) before reporting,
one "Cannot show ‹original goal›: ‹reason›" line per leftover goal, with the reason
coming from describeStuckModuleTypingGoal.
Equations
- GaudisCrypt.tacticModuletyping! = Lean.ParserDescr.node `GaudisCrypt.tacticModuletyping! 1024 (Lean.ParserDescr.nonReservedSymbol "moduletyping!" false)
Instances For
normalmodule #
One step of normalmodule's core script, factored out so normalmodule can require it to
fire at least once (see below) while still repeating it leniently afterwards.
Equations
- GaudisCrypt.tacticNormalmoduleStep = Lean.ParserDescr.node `GaudisCrypt.tacticNormalmoduleStep 1024 (Lean.ParserDescr.nonReservedSymbol "normalmoduleStep" false)
Instances For
Syntax-directed closer for Normal m / Neutral m: peels the constructor matching m's
head, disambiguating .app's two constructors by checking whether the function side is a
literal .procHoles node, and leaves whatever it cannot close open. Fails outright if it
cannot make even one step of progress (e.g. Neutral (.pair _ _), which no constructor can
ever produce) rather than silently no-op'ing. See normalmodule! for a variant that fails
loudly with a diagnosed reason on every unclosed goal, not just a wholly-stuck one.
Equations
- GaudisCrypt.tacticNormalmodule = Lean.ParserDescr.node `GaudisCrypt.tacticNormalmodule 1024 (Lean.ParserDescr.nonReservedSymbol "normalmodule" false)
Instances For
normalmodule, but fails loudly instead of leaving unclosed goals open. Not fail-fast: it
still runs the core script to completion (closing everything it can) before reporting, one
"Cannot show ‹original goal›: ‹reason›" line per leftover goal, with the reason coming from
describeStuckNormalModuleGoal.
Equations
- GaudisCrypt.tacticNormalmodule! = Lean.ParserDescr.node `GaudisCrypt.tacticNormalmodule! 1024 (Lean.ParserDescr.nonReservedSymbol "normalmodule!" false)
Instances For
reduce_simp #
Equations
- GaudisCrypt.tacticReduce_simp_head = Lean.ParserDescr.node `GaudisCrypt.tacticReduce_simp_head 1024 (Lean.ParserDescr.nonReservedSymbol "reduce_simp_head" false)
Instances For
Fully normalize reduce m subterms — one reduction step at a time via reduce_simp_head,
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
reduce_simp_head branch matches on: unfolding it is what turns that result back into a
constructor tree the next step can work on.
Equations
- GaudisCrypt.tacticReduce_simp = Lean.ParserDescr.node `GaudisCrypt.tacticReduce_simp 1024 (Lean.ParserDescr.nonReservedSymbol "reduce_simp" false)