Scalings: the semantic foundation #
Unit correctness is not type safety. Kennedy makes the point sharply (WMM 2008): "What 'goes wrong' if a program contains a unit error? Nothing!" Under an erased semantics a unit error yields a wrong number, not a stuck program, so progress and preservation there say nothing about units; the semantic content of unit correctness is invariance of program behavior under scaling.
This file builds the object that invariance is stated against.
Scalings are linear functionals #
A scaling assigns a positive real factor to each base unit and each unit
variable, and extends to a homomorphism UExp → (ℝ⁺, ×). Since (ℝ⁺, ×) ≅ (ℝ, +)
under log, and a unit expression is an exponent vector, the extension is a
dot product:
logScale ψ t = Σ_b t.base b · ψ.base b + Σ_i t.vars i · ψ.vars i
Working in log space keeps everything linear, makes positivity free (the actual
factor is exp of this, so it is positive by construction), and turns the
homomorphism laws into linearity.
Why the substitution lemma is easy here #
Kennedy singled out the substitution lemma as the painful part of his Coq development:
"First attempt in Coq: … This doesn't even type-check! Type-checker needs to know
usem τ = usem (subst ty s τ). Solution: explicit equality coercions."
Kennedy also represents units by exponent vectors. The equality coercions arise when object-language types are interpreted as Coq types, so unit substitution changes the type indexing a denotation (Kennedy, WMM 2008).
Here logScale_subst is an identity between two sums because substitution on
exponent vectors is linear. Type interpretation still requires transport; the
family-indexed denotation in Fundamental handles that separately.
Extend a scaling to a unit expression. In log space this is the dot product of the exponent vector with the scaling vector.
Instances For
The trivial scaling: every factor 1, every logarithm 0. The unit
of composition (Scaling.comp_zero), and the scaling that fixes every value
(scaleEnv_zero); holding one parameter of the twisted scaling law at zero
specializes it to moving the valuation alone or the values alone.
Equations
- LambdaS.Scaling.zero = { base := fun (x : B) => 0, vars := fun (x : Fin k) => 0 }
Instances For
The homomorphism laws #
Substitution #
The lemma Kennedy called out as the awkward part of mechanizing this.
The substitution lemma. Scaling a substituted expression is the same as scaling the original under an environment extended by the scale of the substituted expression.
Pure exponent arithmetic: no coercions, no transport.
Pulling a scaling back along a substitution #
logScale_subst and logScale_weaken are both instances of one fact, and the
general form is what a denotation of unit-polymorphic terms needs: a scaling of
the target scope induces one on the source, by scaling what each variable was
substituted by.
The substitution lemma, in general. Scaling a substituted expression is scaling the original under the pulled-back scaling.
Pure exponent arithmetic, as before: substitution is a linear map and logScale
is a linear functional, so this is a change in summation order.
Pulling back along an instantiation extends by the instantiated unit's
magnitude. This is logScale_subst read as a statement about scalings.
The trivial scaling #
Scaling everything by 1: the identity, and the scaling at which
free_theorem_sqr reads a closed polymorphic type.
The scaling that changes nothing: Scaling.zero with B and k explicit.
Equations
- LambdaS.Scaling.id B k = { base := fun (x : B) => 0, vars := fun (x : Fin k) => 0 }