Documentation

LambdaS.Syntax

Syntax of Λs #

Kennedy's Λu attaches units to scalars. Λs also distributes them over an index type: vectors and linear maps carry a space of units.

Representation choices #

Three choices shape the representation.

Unit expressions are exponent vectors, not trees. A unit expression with k unit variables in scope is a Term B (Fin k): the very structure LambdaS.Unify solves systems over. Exponents are rational, so the units form a free ℚ-vector space written multiplicatively (LambdaS.Uom), and a normal form is an exponent vector: equality is vector equality, with no normalization pass to write or verify. m * s / m and s are literally the same object.

Scope is a type index. Ty B D j k and Tm B D j k carry the numbers of enclosing ∀δ and ∀u:d binders. These scope indices rule out dangling unit and dimension variables. They do not rule out selecting the wrong variable within a scope; the substitution composition law exposed such a capture error. Term variables are natural numbers whose scope is checked by context lookup.

A space is the list of units its components carry. This is LambdaS.Space at a finite index type, written structurally: a space is its unit assignment. Ordering is the indexing: [m, s] and [s, m] are isomorphic but not equal, because a linear map's entry (j,i) carries δ_W(j)/δ_V(i) and identifying them would let a matrix's columns permute silently.

Explicit typing, and why it matters here #

Λu.e and e[μ] are written, not inferred, exactly as in Kennedy's Λu. So the checker needs substitution and decidable equality but never unification, types stay unique, and infer is provably complete as well as sound. The unification development in LambdaS.Unify is for a future inference engine, where let-generalization forces it; this module uses only its Term representation, and nothing here rests on its elimination lemmas.

Unit expressions #

@[reducible, inline]
abbrev LambdaS.UExp (B : Type) (k : ℕ) :

A unit expression with k unit variables in scope.

Equations
Instances For
    theorem LambdaS.Term.ext' {B V : Type} {s t : Term B V} (hb : s.base = t.base) (hv : s.vars = t.vars) :
    s = t
    theorem LambdaS.Term.ext'_iff {B V : Type} {s t : Term B V} :
    s = t ↔ s.base = t.base ∧ s.vars = t.vars
    def LambdaS.Term.one {B V : Type} :
    Term B V

    The trivial unit.

    Equations
    Instances For
      @[instance_reducible]
      instance LambdaS.Term.instOne {B V : Type} :
      One (Term B V)
      Equations
      def LambdaS.Term.mul {B V : Type} (s t : Term B V) :
      Term B V

      Unit multiplication: exponents add.

      Equations
      Instances For
        def LambdaS.Term.inv {B V : Type} (t : Term B V) :
        Term B V

        Unit inverse: exponents negate.

        Equations
        Instances For
          def LambdaS.Term.div {B V : Type} (s t : Term B V) :
          Term B V

          Unit division.

          Equations
          Instances For
            def LambdaS.Term.rpow {B V : Type} (t : Term B V) (q : ℚ) :
            Term B V

            Rational powers. Total: every unit expression has an n-th root for n ≠ 0.

            Equations
            • t.rpow q = { base := fun (b : B) => q * t.base b, vars := fun (v : V) => q * t.vars v }
            Instances For
              @[simp]
              theorem LambdaS.Term.one_base {B V : Type} (b : B) :
              base 1 b = 0
              @[simp]
              theorem LambdaS.Term.one_vars {B V : Type} (v : V) :
              vars 1 v = 0
              @[simp]
              theorem LambdaS.Term.mul_base {B V : Type} (s t : Term B V) (b : B) :
              (s.mul t).base b = s.base b + t.base b
              @[simp]
              theorem LambdaS.Term.mul_vars {B V : Type} (s t : Term B V) (v : V) :
              (s.mul t).vars v = s.vars v + t.vars v
              @[simp]
              theorem LambdaS.Term.inv_base {B V : Type} (t : Term B V) (b : B) :
              t.inv.base b = -t.base b
              @[simp]
              theorem LambdaS.Term.inv_vars {B V : Type} (t : Term B V) (v : V) :
              t.inv.vars v = -t.vars v
              @[simp]
              theorem LambdaS.Term.div_base {B V : Type} (s t : Term B V) (b : B) :
              (s.div t).base b = s.base b - t.base b
              @[simp]
              theorem LambdaS.Term.div_vars {B V : Type} (s t : Term B V) (v : V) :
              (s.div t).vars v = s.vars v - t.vars v
              @[simp]
              theorem LambdaS.Term.rpow_base {B V : Type} (t : Term B V) (q : ℚ) (b : B) :
              (t.rpow q).base b = q * t.base b
              @[simp]
              theorem LambdaS.Term.rpow_vars {B V : Type} (t : Term B V) (q : ℚ) (v : V) :
              (t.rpow q).vars v = q * t.vars v
              def LambdaS.Term.ofBase {B V : Type} [DecidableEq B] (b₀ : B) :
              Term B V

              A base unit as a unit expression.

              Equations
              Instances For
                def LambdaS.Term.ofVar {B V : Type} [DecidableEq V] (v₀ : V) :
                Term B V

                A unit variable as a unit expression.

                Equations
                Instances For
                  def LambdaS.UExp.subst {B : Type} {k : ℕ} (t : UExp B (k + 1)) (σ : UExp B k) :
                  UExp B k

                  Substitute σ for de Bruijn unit variable 0, discharging one binder.

                  Linear in the exponents, because substitution into an element of a free ℚ-vector space is a linear map.

                  Equations
                  Instances For
                    def LambdaS.UExp.weaken {B : Type} {k : ℕ} (t : UExp B k) :
                    UExp B (k + 1)

                    Introduce a fresh unit variable, unused.

                    Equations
                    Instances For
                      @[simp]
                      theorem LambdaS.UExp.subst_weaken {B : Type} {k : ℕ} (t σ : UExp B k) :
                      t.weaken.subst σ = t

                      Substituting into a weakened expression changes nothing: the fresh variable was unused.

                      Simultaneous substitution #

                      Single-variable substitution is not closed under going beneath a binder: pushing σ into ∀u. τ must shift σ past the new variable and leave the bound one alone. The standard fix is to define everything from a simultaneous substitution, and to define single substitution and weakening as instances of it.

                      Λs gets that cheaply. Simultaneous substitution is a linear map on exponent vectors, so its identity, composition and homomorphism laws are Finset arithmetic rather than structural inductions. Only the walk over types is structural, and there the sole interesting case is the quantifier.

                      def LambdaS.substU {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u : UExp B k) :
                      UExp B k₀

                      Substitute a unit expression for every unit variable at once.

                      Equations
                      Instances For
                        @[simp]
                        theorem LambdaS.substU_base {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u : UExp B k) (b : B) :
                        (substU η u).base b = u.base b + ∑ i : Fin k, u.vars i * (η i).base b
                        @[simp]
                        theorem LambdaS.substU_vars {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u : UExp B k) (v : Fin k₀) :
                        (substU η u).vars v = ∑ i : Fin k, u.vars i * (η i).vars v
                        def LambdaS.idU (B : Type) (k : ℕ) :
                        Fin k → UExp B k

                        The identity substitution.

                        Equations
                        Instances For
                          def LambdaS.liftU {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) :
                          Fin (k + 1) → UExp B (k₀ + 1)

                          Extend a substitution under a binder: the bound variable maps to itself and everything else is shifted past it. This clause is the whole content of the de Bruijn discipline.

                          Equations
                          Instances For
                            @[simp]
                            theorem LambdaS.substU_ofVar {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (i : Fin k) :
                            substU η (Term.ofVar i) = η i
                            @[simp]
                            theorem LambdaS.substU_id {B : Type} {k : ℕ} (u : UExp B k) :
                            substU (idU B k) u = u
                            @[simp]
                            theorem LambdaS.liftU_id {B : Type} {k : ℕ} :
                            liftU (idU B k) = idU B (k + 1)
                            def LambdaS.nilU (B : Type) :
                            Fin 0 → UExp B 0

                            The empty substitution, at the closed scope.

                            Equations
                            Instances For
                              @[simp]
                              theorem LambdaS.substU_nil {B : Type} (u : UExp B 0) :
                              substU (nilU B) u = u
                              @[simp]
                              theorem LambdaS.map_substU_id {B : Type} {k : ℕ} (V : List (UExp B k)) :
                              List.map (substU (idU B k)) V = V
                              @[simp]
                              theorem LambdaS.substU_one {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) :
                              substU η 1 = 1

                              Substitution is a homomorphism of the unit group, which is what lets a typing rule that multiplies units survive it.

                              theorem LambdaS.substU_mul {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u w : UExp B k) :
                              substU η (Term.mul u w) = Term.mul (substU η u) (substU η w)
                              theorem LambdaS.substU_div {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u w : UExp B k) :
                              substU η (Term.div u w) = Term.div (substU η u) (substU η w)
                              theorem LambdaS.substU_rpow {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u : UExp B k) (q : ℚ) :
                              substU η (Term.rpow u q) = Term.rpow (substU η u) q
                              theorem LambdaS.substU_comp {B : Type} {k k₁ k₂ : ℕ} (η₂ : Fin k₁ → UExp B k₂) (η₁ : Fin k → UExp B k₁) (u : UExp B k) :
                              substU η₂ (substU η₁ u) = substU (fun (i : Fin k) => substU η₂ (η₁ i)) u

                              Composition. The lemma a substitution development usually pays for with a structural induction; here both sides are linear maps.

                              theorem LambdaS.substU_weaken {B : Type} {k k₀ : ℕ} (η : Fin k → UExp B k₀) (u : UExp B k) :
                              theorem LambdaS.substU_weaken_cons {B : Type} {k k₀ : ℕ} (w : UExp B k₀) (ξ : Fin k → UExp B k₀) (t : UExp B k) :
                              substU (Fin.cons w ξ) t.weaken = substU ξ t

                              Substituting into a weakened expression ignores whatever was put at index zero: the weakened expression does not mention it.

                              theorem LambdaS.liftU_comp {B : Type} {k k₁ k₂ : ℕ} (η₂ : Fin k₁ → UExp B k₂) (η₁ : Fin k → UExp B k₁) :
                              (liftU fun (i : Fin k) => substU η₂ (η₁ i)) = fun (i : Fin (k + 1)) => substU (liftU η₂) (liftU η₁ i)

                              Spaces #

                              @[reducible, inline]
                              abbrev LambdaS.Sp (B : Type) (k : ℕ) :

                              A space with k unit variables in scope: the list of units carried by its components. The index type is Fin V.length, the assignment is V.get.

                              Equations
                              Instances For
                                def LambdaS.Sp.dual {B : Type} {k : ℕ} (V : Sp B k) :
                                Sp B k

                                The dual space carries reciprocal units.

                                Equations
                                Instances For
                                  def LambdaS.Sp.scale {B : Type} {k : ℕ} (V : Sp B k) (d : UExp B k) :
                                  Sp B k

                                  Scaling a space by a unit.

                                  Equations
                                  Instances For
                                    def LambdaS.Sp.subst {B : Type} {k : ℕ} (V : Sp B (k + 1)) (σ : UExp B k) :
                                    Sp B k

                                    Substitute through a space.

                                    Equations
                                    Instances For
                                      def LambdaS.Sp.weaken {B : Type} {k : ℕ} (V : Sp B k) :
                                      Sp B (k + 1)

                                      Weaken a space.

                                      Equations
                                      Instances For
                                        @[simp]
                                        theorem LambdaS.Sp.subst_weaken {B : Type} {k : ℕ} (V : Sp B k) (σ : UExp B k) :
                                        V.weaken.subst σ = V

                                        The bridge to the model #

                                        Hart's classification (LambdaS.Map) is developed over units of the model, Uom B, and spaces of the model, Space B I. A closed unit expression is a unit of the model outright (the same exponent vector), a closed space is a space of the model indexed by position, and the unit the row at codomain unit w carries at component i under T-MCons is the model's entry.

                                        def LambdaS.UExp.toUom {B : Type} (t : UExp B 0) :
                                        Uom B

                                        A closed unit expression as a unit of the model: its exponent vector.

                                        Equations
                                        Instances For
                                          @[simp]
                                          theorem LambdaS.UExp.exp_toUom {B : Type} (t : UExp B 0) (b : B) :
                                          t.toUom.exp b = t.base b

                                          The embedding is injective: with no unit variables in scope, a unit expression is its exponent vector.

                                          @[simp]
                                          theorem LambdaS.UExp.toUom_one {B : Type} :
                                          toUom 1 = 1
                                          @[simp]
                                          theorem LambdaS.UExp.toUom_mul {B : Type} (s t : UExp B 0) :
                                          @[simp]
                                          theorem LambdaS.UExp.toUom_inv {B : Type} (t : UExp B 0) :
                                          @[simp]
                                          theorem LambdaS.UExp.toUom_div {B : Type} (s t : UExp B 0) :
                                          @[simp]
                                          theorem LambdaS.UExp.toUom_rpow {B : Type} (t : UExp B 0) (q : ℚ) :
                                          toUom (Term.rpow t q) = t.toUom ^ q
                                          def LambdaS.Sp.toSpace {B : Type} (V : Sp B 0) :

                                          A closed space as a space of the model, indexed by position.

                                          Equations
                                          Instances For
                                            def LambdaS.linEntry {B : Type} {k : ℕ} (V W : Sp B k) (j : Fin (List.length W)) (i : Fin (List.length V)) :
                                            UExp B k

                                            The unit of entry (j, i) of a matrix at Lin V W, read off the two spaces: W_j / V_i.

                                            Equations
                                            Instances For
                                              theorem LambdaS.entry_toSpace {B : Type} (V W : Sp B 0) (j : Fin (List.length W)) (i : Fin (List.length V)) :
                                              entry V.toSpace W.toSpace j i = (linEntry V W j i).toUom

                                              The calculus's entry units are the model's. For closed spaces, the unit Lin V W assigns entry (j, i) is entry of LambdaS.Map at the two spaces, so Hart's classification, developed in the model, is about the matrices the calculus types.

                                              Dimensions #

                                              A dimension expression has exactly the structure a unit expression does: the dimensions form a free ℚ-vector space on base dimensions, written multiplicatively, just as the units do on base units. So DExp is UExp at the dimension alphabet, and every operation (multiplication, division, rational powers, substitution, weakening, decidable equality) is reused rather than rebuilt.

                                              dimension Velocity = Length/Time is therefore Term.div, an abbreviation with nothing left to check. Unit definitions are the interesting case and live in LambdaS.Declare, because unit yard = 3 foot declares a generator and an equation, and equations can conflict.

                                              Why dimension variables #

                                              The alternative is two unit quantifiers, ∀u and ∀u:d. It does not work. An unbounded ∀u still has to say something about u's dimension, and there is no right answer: reporting the trivial dimension claims u is dimensionless, so convert x u 1 typechecks under a binder that promised nothing; reporting "unknown" makes dimOf partial and threads Option through every dimension lemma.

                                              Abstracting the dimension is the answer, and it collapses the two quantifiers into one:

                                              ∀u. τ ≡ ∀δ. ∀u:δ. τ

                                              Unbounded quantification is bounded quantification at a dimension variable. dimOf stays total and there is a single quantifier rule to state and prove. Under ∀δ. ∀u:δ, conversion out of u to a concrete unit is rejected, because no concrete unit has dimension δ. Conversion to another variable bounded by the same δ is not rejected: that is the generic caster, which typechecks (Examples.caster).

                                              @[reducible, inline]
                                              abbrev LambdaS.DExp (D : Type) (j : ℕ) :

                                              A dimension expression with j dimension variables in scope. Literally a unit expression over base dimensions: the two are the same free ℚ-vector space construction, so UExp's whole API applies.

                                              Equations
                                              Instances For
                                                class LambdaS.UnitSys (B D : Type) :

                                                A unit system: the dimension each base unit measures.

                                                dim need not be injective: meter and foot share a dimension, which is what lets Λs avoid restricting compound units to one named unit per dimension. Nor need it land on a generator: joule can be declared directly at Mass·Length²·Time⁻² rather than needing a base dimension of its own, which is what makes dimension definitions worth having.

                                                Base units have closed dimensions: a generator's dimension cannot mention a dimension variable, since those are bound by types.

                                                • dim : B → DExp D 0

                                                  The dimension each base unit measures.

                                                Instances
                                                  @[reducible, inline]
                                                  abbrev LambdaS.DCtx (D : Type) (j k : ℕ) :

                                                  A dimension context: the declared dimension of each unit variable in scope. ∀u:d. τ extends it by d.

                                                  Equations
                                                  Instances For
                                                    def LambdaS.DCtx.nil (D : Type) :
                                                    DCtx D 0 0

                                                    The empty context: a closed term has no unit variables in scope.

                                                    Equations
                                                    Instances For
                                                      def LambdaS.DCtx.cons {D : Type} {j k : ℕ} (d : DExp D j) (Δ : DCtx D j k) :
                                                      DCtx D j (k + 1)

                                                      Extend under a unit binder.

                                                      Equations
                                                      Instances For
                                                        def LambdaS.DCtx.weakenDim {D : Type} {j k : ℕ} (Δ : DCtx D j k) :
                                                        DCtx D (j + 1) k

                                                        Extend under a dimension binder: every declared dimension is weakened into the larger dimension scope.

                                                        Equations
                                                        Instances For
                                                          def LambdaS.DCtx.substDim {D : Type} {j k : ℕ} (Δ : DCtx D (j + 1) k) (σ : DExp D j) :
                                                          DCtx D j k

                                                          Discharge a dimension binder.

                                                          Equations
                                                          Instances For
                                                            @[simp]
                                                            theorem LambdaS.DCtx.substDim_weakenDim {D : Type} {j k : ℕ} (Δ : DCtx D j k) (σ : DExp D j) :
                                                            def LambdaS.dimOf {B D : Type} [Fintype B] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (u : UExp B k) :
                                                            DExp D j

                                                            The dimension of a unit expression, relative to a dimension context.

                                                            Linear in the exponent vector: a matrix product, with the naive "each base unit has one base dimension" the special case where every column is a standard basis vector. Base units contribute only to base dimensions; the dimension variables of the result come entirely from the context.

                                                            Equations
                                                            • One or more equations did not get rendered due to their size.
                                                            Instances For
                                                              @[simp]
                                                              theorem LambdaS.dimOf_ofVar {B D : Type} [Fintype B] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (i : Fin k) :
                                                              dimOf Δ (Term.ofVar i) = Δ i

                                                              A unit variable has exactly its declared dimension.

                                                              @[simp]
                                                              theorem LambdaS.dimOf_weakenDim {B D : Type} [Fintype B] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (u : UExp B k) :

                                                              Weakening the dimension context weakens the dimension.

                                                              @[simp]
                                                              theorem LambdaS.dimOf_weaken_cons {B D : Type} [Fintype B] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (e : DExp D j) (u : UExp B k) :
                                                              dimOf (DCtx.cons e Δ) u.weaken = dimOf Δ u

                                                              A weakened unit has the dimension it had before the context grew.

                                                              @[simp]
                                                              theorem LambdaS.substU_shift {B : Type} {k : ℕ} (t : UExp B k) :
                                                              substU (fun (i : Fin k) => Term.ofVar i.succ) t = t.weaken

                                                              Substituting the shift is weakening.

                                                              def LambdaS.SameDim {B D : Type} [Fintype B] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (u v : UExp B k) :

                                                              Two units are interchangeable when they have the same dimension under Δ. Nothing further is required of their unit variables: Δ supplies each variable's dimension, so convert works under a bounded quantifier, which is what makes a dimension-typed quantity usable.

                                                              Equations
                                                              Instances For
                                                                @[instance_reducible]
                                                                instance LambdaS.instDecidableSameDimOfFintypeOfDecidableEq {B D : Type} [Fintype B] [Fintype D] [DecidableEq D] [UnitSys B D] {j k : ℕ} (Δ : DCtx D j k) (u v : UExp B k) :
                                                                Equations

                                                                Types #

                                                                inductive LambdaS.Ty (B D : Type) :
                                                                ℕ → ℕ → Type

                                                                Types of Λs, indexed by the number of dimension variables and the number of unit variables in scope.

                                                                Six formers, one unit quantifier. ∀u. τ is not primitive: it is ∀δ. ∀u:δ. τ, and the free theorems that need a genuinely unconstrained unit get one by abstracting its dimension.

                                                                • Q {B D : Type} {j k : ℕ} : UExp B k → Ty B D j k

                                                                  A scalar quantity carrying a unit.

                                                                • arrow {B D : Type} {j k : ℕ} : Ty B D j k → Ty B D j k → Ty B D j k
                                                                • vec {B D : Type} {j k : ℕ} : Sp B k → Ty B D j k

                                                                  A vector over a space.

                                                                • lin {B D : Type} {j k : ℕ} : Sp B k → Sp B k → Ty B D j k

                                                                  A linear map V ⊸ W. Entry (j,i) carries δ_W(j) / δ_V(i), which is Hart's rank-one condition holding by construction.

                                                                • all {B D : Type} {j k : ℕ} : DExp D j → Ty B D j (k + 1) → Ty B D j k

                                                                  Unit polymorphism bounded by a dimension, ∀u:d. τ.

                                                                • allDim {B D : Type} {j k : ℕ} : Ty B D (j + 1) k → Ty B D j k

                                                                  Dimension polymorphism, ∀δ. τ. Together with all this is unbounded unit quantification.

                                                                Instances For
                                                                  def LambdaS.Ty.ground {B D : Type} {j k j₀ k₀ : ℕ} :
                                                                  (Fin k → UExp B k₀) → (Fin j → DExp D j₀) → Ty B D j k → Ty B D j₀ k₀

                                                                  Push a unit substitution and a dimension substitution through a type.

                                                                  Every other substitution on types is an instance of this one. The quantifier cases are the only interesting clauses: going under ∀u:d must map the bound variable to itself and shift everything else past it, which is exactly liftU.

                                                                  Equations
                                                                  Instances For
                                                                    def LambdaS.Ty.subst {B D : Type} {j k : ℕ} (τ : Ty B D j (k + 1)) (σ : UExp B k) :
                                                                    Ty B D j k

                                                                    Substitute a unit expression for the outermost unit variable.

                                                                    Equations
                                                                    Instances For
                                                                      def LambdaS.Ty.weaken {B D : Type} {j k : ℕ} (τ : Ty B D j k) :
                                                                      Ty B D j (k + 1)

                                                                      Weaken into a larger unit-variable scope.

                                                                      Equations
                                                                      Instances For
                                                                        def LambdaS.Ty.substDim {B D : Type} {j k : ℕ} (τ : Ty B D (j + 1) k) (σ : DExp D j) :
                                                                        Ty B D j k

                                                                        Substitute a dimension expression for the outermost dimension variable. Units are untouched: a unit expression cannot mention a dimension variable.

                                                                        Equations
                                                                        Instances For
                                                                          def LambdaS.Ty.weakenDim {B D : Type} {j k : ℕ} (τ : Ty B D j k) :
                                                                          Ty B D (j + 1) k

                                                                          Weaken into a larger dimension-variable scope.

                                                                          Equations
                                                                          Instances For
                                                                            @[simp]
                                                                            theorem LambdaS.Ty.ground_id {B D : Type} {j k : ℕ} (τ : Ty B D j k) :
                                                                            ground (idU B k) (idU D j) τ = τ

                                                                            Grounding by the identity does nothing.

                                                                            theorem LambdaS.Ty.ground_comp {B D : Type} {j k j₁ k₁ j₂ k₂ : ℕ} (η₂ : Fin k₁ → UExp B k₂) (δ₂ : Fin j₁ → DExp D j₂) (η₁ : Fin k → UExp B k₁) (δ₁ : Fin j → DExp D j₁) (τ : Ty B D j k) :
                                                                            ground η₂ δ₂ (ground η₁ δ₁ τ) = ground (fun (i : Fin k) => substU η₂ (η₁ i)) (fun (i : Fin j) => substU δ₂ (δ₁ i)) τ

                                                                            Grounding composes. Two substitutions in sequence are the substitution that maps each variable through both.

                                                                            def LambdaS.Ty.decEq {B D : Type} [Fintype B] [DecidableEq B] [Fintype D] [DecidableEq D] {j k : ℕ} (a b : Ty B D j k) :
                                                                            Decidable (a = b)

                                                                            Decidable equality, by structural recursion. Written by hand rather than derived: the deriving handler cannot carry the [Fintype B] constraint that DecidableEq (UExp B k) needs.

                                                                            Equations
                                                                            Instances For
                                                                              theorem LambdaS.Ty.ground_weaken {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (w : UExp B k₀) (σ : Ty B D j k) :
                                                                              ground (Fin.cons w η) δ σ.weaken = ground η δ σ

                                                                              Weakening then grounding by an extended environment ignores the extension: the weakened type does not mention the new variable. This is what lets a context captured outside a unit binder be reused inside it.

                                                                              theorem LambdaS.Ty.ground_liftU_subst {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (w : UExp B k₀) (τ : Ty B D j (k + 1)) :
                                                                              (ground (liftU η) δ τ).subst w = ground (Fin.cons w η) δ τ

                                                                              The quantifier case. Grounding under a binder and then substituting the instantiating unit is the same as grounding with the environment extended by it.

                                                                              This is the lemma the uapp case of soundness turns on.

                                                                              theorem LambdaS.Ty.ground_weakenDim {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (w : DExp D j₀) (σ : Ty B D j k) :
                                                                              ground η (Fin.cons w δ) σ.weakenDim = ground η δ σ

                                                                              The dimension analogue of ground_weaken.

                                                                              theorem LambdaS.Ty.ground_liftD_substDim {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (w : DExp D j₀) (τ : Ty B D (j + 1) k) :
                                                                              (ground η (liftU δ) τ).substDim w = ground η (Fin.cons w δ) τ

                                                                              The dimension analogue of ground_liftU_subst.

                                                                              theorem LambdaS.Ty.ground_subst {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (μ : UExp B k) (τ : Ty B D j (k + 1)) :
                                                                              ground η δ (τ.subst μ) = ground (Fin.cons (substU η μ) η) δ τ

                                                                              Grounding a single substitution: the same as grounding with the environment extended by the grounded instantiation. This is what turns uapp's result type into something the closure's own environment can produce.

                                                                              theorem LambdaS.Ty.ground_substDim {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (d : DExp D j) (τ : Ty B D (j + 1) k) :
                                                                              ground η δ (τ.substDim d) = ground η (Fin.cons (substU δ d) δ) τ

                                                                              The dimension analogue.

                                                                              The skeleton #

                                                                              Erase every unit and dimension and a type becomes a plain simple type. Nothing in the calculus's shape depends on units, which is why substituting one leaves the skeleton alone. That is what makes the normalization argument in LambdaS.Normalization Tait's rather than Girard's.

                                                                              def LambdaS.Ty.skel {B D : Type} {j k : ℕ} :
                                                                              Ty B D j k → ℕ

                                                                              The size of a type's skeleton.

                                                                              Equations
                                                                              Instances For
                                                                                @[simp]
                                                                                theorem LambdaS.Ty.skel_ground {B D : Type} {j k j₀ k₀ : ℕ} (η : Fin k → UExp B k₀) (δ : Fin j → DExp D j₀) (τ : Ty B D j k) :
                                                                                (ground η δ τ).skel = τ.skel

                                                                                Substitution does not change the skeleton.

                                                                                @[simp]
                                                                                theorem LambdaS.Ty.skel_subst {B D : Type} {j k : ℕ} (τ : Ty B D j (k + 1)) (μ : UExp B k) :
                                                                                (τ.subst μ).skel = τ.skel
                                                                                @[simp]
                                                                                theorem LambdaS.Ty.skel_substDim {B D : Type} {j k : ℕ} (τ : Ty B D (j + 1) k) (d : DExp D j) :
                                                                                (τ.substDim d).skel = τ.skel
                                                                                @[simp]
                                                                                theorem LambdaS.Ty.subst_weaken {B D : Type} {j k : ℕ} (τ : Ty B D j k) (σ : UExp B k) :
                                                                                τ.weaken.subst σ = τ

                                                                                Substituting into a weakened type changes nothing. The type-level statement that a unit binder binds a genuinely fresh variable.

                                                                                With subst and weaken both built from ground, this is composition plus the identity law rather than a structural induction of its own.

                                                                                @[simp]
                                                                                theorem LambdaS.Ty.substDim_weakenDim {B D : Type} {j k : ℕ} (τ : Ty B D j k) (σ : DExp D j) :

                                                                                The same for the dimension binder.

                                                                                Terms #

                                                                                inductive LambdaS.Tm (B D : Type) :
                                                                                ℕ → ℕ → Type

                                                                                Terms of Λs, with de Bruijn indices for value variables and type indices for the dimension- and unit-variable scopes.

                                                                                • var {B D : Type} {j k : ℕ} : ℕ → Tm B D j k
                                                                                • lam {B D : Type} {j k : ℕ} : Ty B D j k → Tm B D j k → Tm B D j k
                                                                                • app {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k
                                                                                • lit {B D : Type} {j k : ℕ} : ℚ → Tm B D j k

                                                                                  Every rational literal has unit 1, including zero.

                                                                                • ucon {B D : Type} {j k : ℕ} : UExp B k → Tm B D j k

                                                                                  A unit constant, so m : Q m. This is what makes 1.3 m work.

                                                                                • mul {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k
                                                                                • div {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k
                                                                                • add {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  Addition, where unit errors are caught.

                                                                                • pow {B D : Type} {j k : ℕ} : ℚ → Tm B D j k → Tm B D j k

                                                                                  A constant rational power, e^q. Primitive, because it is not definable from the field operations; total, with pow 0 e : Q 1 denoting x^0 = 1. It mirrors the unit grammar's u^q: pow (1/n) is the n-th root.

                                                                                • idx {B D : Type} {j k : ℕ} : Tm B D j k → ℕ → Tm B D j k

                                                                                  Component extraction, e.i: the elimination form for Vec.

                                                                                • mrow {B D : Type} {j k : ℕ} : Tm B D j k → ℕ → Tm B D j k

                                                                                  Row extraction: an elimination form for Lin, dual to mcons as idx is to vcons. Row i of a map at Lin V W is a vector over the row space w / δ_V(·) for w = δ_W(i), which is exactly the vector mcons consumes, so extraction and introduction agree.

                                                                                • ifle {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  Compare and branch, fused so that no Bool type is needed. The scrutinees are compared at a common unit, which is what keeps the form parametric: a rescaling multiplies both by the same positive factor and the ordering survives (OrderedNum.le_scale). Comparing across units does not typecheck, so the observation that could detect a rescaling is unreachable by construction.

                                                                                • mapp {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  Linear-map application, e ⊙ e, distinct from function application app.

                                                                                • comp {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  Composition of linear maps, e ∘ e.

                                                                                • vnil {B D : Type} {j k : ℕ} : Tm B D j k

                                                                                  The empty vector, at the empty space.

                                                                                • vcons {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  A scalar consed onto a vector: the vector introduction step. The unit of the new component is read off the scalar's type, so the constructor carries no annotation.

                                                                                • mnil {B D : Type} {j k : ℕ} : Sp B k → Tm B D j k

                                                                                  The zero-row matrix, carrying its column space. The annotation is the design point: a matrix with no rows still has a width, and nothing else could supply it.

                                                                                • mcons {B D : Type} {j k : ℕ} : UExp B k → Tm B D j k → Tm B D j k → Tm B D j k

                                                                                  A row consed onto a matrix: output unit w, a row (a vector term whose components carry w / δ_V(i)), and the rest of the matrix. The annotation w is needed because a row over an empty column space determines no output unit.

                                                                                • log {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k

                                                                                  Logarithm. Requires an argument at unit 1. A probability density whose base measure has nontrivial dimension therefore cannot be passed directly to log; it must first be expressed relative to a reference density.

                                                                                • exp {B D : Type} {j k : ℕ} : Tm B D j k → Tm B D j k

                                                                                  Exponential. Also requires an argument at unit 1.

                                                                                  In the quantum examples, cancellation in E·t/ħ yields exactly this unit; dimensional equality alone would not establish the typing premise.

                                                                                • ulam {B D : Type} {j k : ℕ} : DExp D j → Tm B D j (k + 1) → Tm B D j k

                                                                                  Unit abstraction, Λu:d. e.

                                                                                • uapp {B D : Type} {j k : ℕ} : Tm B D j k → UExp B k → Tm B D j k

                                                                                  Unit application, e[μ]. Checks that μ has the declared dimension.

                                                                                • dlam {B D : Type} {j k : ℕ} : Tm B D (j + 1) k → Tm B D j k

                                                                                  Dimension abstraction, Λδ. e.

                                                                                • dapp {B D : Type} {j k : ℕ} : Tm B D j k → DExp D j → Tm B D j k

                                                                                  Dimension application, e{d}.

                                                                                • convert {B D : Type} {j k : ℕ} : Tm B D j k → UExp B k → UExp B k → Tm B D j k

                                                                                  Conversion, e in v, written convert e u v with the source unit u carried explicitly.

                                                                                  A term constructor rather than sugar for a multiplication, so that the trusted core checks the dimensions and determines the factor. Under the elaboration alternative a wrong factor would still typecheck, putting the Mars Climate Orbiter failure outside the trusted boundary.

                                                                                  This is the only form that reads a conversion factor, and its cost is coherence: a parametric term containing it is invariant under coherent rescalings (fundamental) rather than under all of them (fundamental_free). The unit constant ucon is excluded from both theorems.

                                                                                Instances For
                                                                                  @[reducible, inline]
                                                                                  abbrev LambdaS.Ctx (B D : Type) (j k : ℕ) :

                                                                                  A typing context.

                                                                                  Equations
                                                                                  Instances For
                                                                                    def LambdaS.Ctx.weaken {B D : Type} {j k : ℕ} (Γ : Ctx B D j k) :
                                                                                    Ctx B D j (k + 1)

                                                                                    Weakening under a unit binder.

                                                                                    Equations
                                                                                    Instances For
                                                                                      def LambdaS.Ctx.weakenDim {B D : Type} {j k : ℕ} (Γ : Ctx B D j k) :
                                                                                      Ctx B D (j + 1) k

                                                                                      Weakening under a dimension binder.

                                                                                      Equations
                                                                                      Instances For