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!" A unit error does not get a program stuck; it gets you a wrong number. So progress and preservation are vacuous here, and the semantic content of unit correctness has to be something else: 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."
That pain comes from unit expressions being syntax trees, so substitution is structural and the semantics has to be transported along it.
Here unit expressions are exponent vectors and substitution is a linear map
on them, so logScale_subst is an identity between two sums: pure arithmetic,
with no coercions and no transport. The representation choice made for
decidability in LambdaS.Syntax pays off again, in a place it was not chosen
for.
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 reference point the Pi theorem
collapses a polymorphic type down to.
The scaling that changes nothing.
Equations
- LambdaS.Scaling.id B k = { base := fun (x : B) => 0, vars := fun (x : Fin k) => 0 }