Documentation

GaudisCrypt.Language.ModuleExpressionBasics

Module expressions, basics #

The raw calculus underlying modules: ModuleTypeRep (the types), the untyped syntax tree ModuleExpression with extrinsic typing (ModuleExpression.HasType), de Bruijn renaming/substitution with the σ-calculus laws they satisfy, call-by-value reduction with its normal forms, and the deciding tactics moduletyping and normalmodule.

This is the part of the development that depends neither on confluence (ModuleExpressionConfluence.lean) nor on normalization (ModuleExpressionNormalization.lean); their consequences — the reduce theory and the reduce_simp tactic — live in ModuleExpression.lean, which is what downstream modules import.

Modules.lean builds Module T — a closed, well-typed, normal expression — on top of that.

Possible types of modules.

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).

      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.

        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
          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
              Instances For

                Single-variable substitution map: 0 ↦ arg, n+1 ↦ var n. Untyped analogue of variableSubstitution.

                Equations
                Instances For

                  Single-variable de Bruijn substitution: replace index 0 in body by arg.

                  Equations
                  Instances For

                    The de Bruijn σ-calculus laws #

                    rename and substituteSimultaneously compose in the four standard ways. Both the confluence and the normalization proof need them to push a renaming or a substitution through a β-contractum.

                    Substituting into a renamed term is substituting along the composite.

                    theorem GaudisCrypt.ModuleExpression.liftSubst_rename (σ : ℕ → ModuleExpression) (ρ : ℕ → ℕ) :
                    (liftSubst fun (k : ℕ) => rename ρ (σ k)) = fun (n : ℕ) => rename (liftRen ρ) (liftSubst σ n)

                    Renaming a substituted term is substituting along the renamed substitution.

                    The identity renaming acts trivially, on any expression (no typing hypothesis needed — contrast HasType.rename_eq_self).

                    theorem GaudisCrypt.ModuleExpression.rename_substitute (ρ : ℕ → ℕ) (body arg : ModuleExpression) :
                    rename ρ (body.substitute arg) = (rename (liftRen ρ) body).substitute (rename ρ arg)

                    Renaming commutes with β-substitution.

                    β-substituting into a lifted simultaneous substitution is one simultaneous substitution: index 0 goes to arg, index n+1 to σ n.

                    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
                    Instances For

                      inst.toModuleExpr is closed, so renaming leaves it alone.

                      Non-deterministic single-step reduction on untyped module expressions.

                      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
                        Instances For

                          Weakening: prepending a binder is the renaming Nat.succ.

                          theorem GaudisCrypt.ModuleExpression.HasType.IsRenaming.lift {Δ Γ : ModuleContext} {A : ModuleTypeRep} {ρ : ℕ → ℕ} (h : IsRenaming Δ Γ ρ) :
                          IsRenaming (A :: Δ) (A :: Γ) (liftRen ρ)

                          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
                          Instances For

                            A well-typed substitution lifts under one binder to liftSubst σ.

                            Simultaneous substitution preserves typing along a well-typed substitution.

                            theorem GaudisCrypt.ModuleExpression.HasType.substitute {Δ : List ModuleTypeRep} {u t : ModuleTypeRep} {body arg : ModuleExpression} (hb : body.HasType (u :: Δ) t) (ha : arg.HasType Δ u) :
                            (body.substitute arg).HasType Δ t

                            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.

                            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
                              @[implicit_reducible]
                              Equations

                              Beta-normal form: no redex anywhere.

                              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).

                                Instances For

                                  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).

                                  Instances For

                                    Shape predicates for call-by-value reduction (untyped analogues) #

                                    Typing is preserved along multi-step reduction.

                                    Whatever reduces has a redex, so it is not normal. Every case is closed by inverting the Normal/Neutral derivation: a redex head (.abs, .pair under .fst/.snd, a .procHoles applied to a proc-tuple) is never neutral, and the congruence cases contradict the induction hypothesis on the reducing subterm.

                                    m terminates: it multi-step reduces to some normal form.

                                    Equations
                                    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
                                        Instances For

                                          Multi-step reduction refines convertibility.

                                          reduce m is always convertible to m (a reduct if terminating, a class rep otherwise).

                                          reduce is constant on a convertible class of non-terminating expressions. (Not private: ModuleExpression.lean needs it for reduce_idempotent/confluence.)

                                          A well-typed proc-tuple is the module-expression image of some instantiation (the existence content of procTupleLookup/procTupleLookup_toModuleTuple, but as a Prop).

                                          Progress for well-typed (possibly open) terms: a non-normal well-typed term reduces.

                                          A normal well-typed closed term is closed-normal.

                                          Misc #

                                          A well-typed closed term is never neutral.

                                          app 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.

                                          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
                                          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
                                            Instances For

                                              normalmodule #

                                              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
                                              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
                                                Instances For