5.2. T1 — What is measured: objects, sorts, and parts
Start at the bottom-left of the square, with the things that bear the properties. The oscillator has two bodies and three springs, so two object types, each a plain inductive with decidable equality:
inductive Body | A | B
deriving DecidableEq, Repr
inductive Spring | kA | kC | kB
deriving DecidableEq, ReprEach object has a sort, the kind of system it is: a point mass, a linear spring, and, for the pair the bodies and springs make up, a coupled oscillator pair. Two further sorts, springs in parallel and springs in series, are declared now and earn their keep in T6:
def bodyS : SortOfSystem := ⟨"point mass"⟩
def springS : SortOfSystem := ⟨"linear spring"⟩
def pairS : SortOfSystem := ⟨"coupled oscillator pair"⟩
def parallelS : SortOfSystem := ⟨"springs in parallel"⟩
def seriesS : SortOfSystem := ⟨"springs in series"⟩The left edge of the square, instantiated by, is one instance per object type, declared once and never derived:
/-- Left edge, *instantiated by*: every body is a point mass. Declared once, never derived. -/
instance : Sorted Body := ⟨fun _ => bodyS⟩
/-- Every spring is a linear spring. -/
instance : Sorted Spring := ⟨fun _ => springS⟩A designation is what lets a quantity name its bearer in words; injectivity is decided:
/-- Designation (so `toIndividualProperty` can name a body); injectivity by `decide`. -/
instance : Designated Body where
designation
| .A => ⟨"body A"⟩
| .B => ⟨"body B"⟩
designation_inj {x y} h := x:Bodyy:Bodyh:(match x with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ x = y
y:Bodyh:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = yy:Bodyh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = y y:Bodyh:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = yy:Bodyh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match y with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = y h:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.Ah:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.B h:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = Body.Ah:(match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.A = Body.Bh:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.Ah:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ Body.B = Body.B first | All goals completed! 🐙 | exact absurd (System.mk.inj h) (h:(match Body.B with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }) =
match Body.A with
| Body.A => { id := "body A" }
| Body.B => { id := "body B" }⊢ ¬"body B" = "body A" All goals completed! 🐙)
instance : Designated Spring where
designation
| .kA => ⟨"spring A"⟩
| .kC => ⟨"spring C"⟩
| .kB => ⟨"spring B"⟩
designation_inj {x y} h := x:Springy:Springh:(match x with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ x = y
y:Springh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = yy:Springh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = yy:Springh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = y y:Springh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = yy:Springh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = yy:Springh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match y with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = y h:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kAh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kCh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kB h:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kAh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kCh:(match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kA = Spring.kBh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kAh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kCh:(match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kC = Spring.kBh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kA with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kAh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kCh:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ Spring.kB = Spring.kB first | All goals completed! 🐙 | exact absurd (System.mk.inj h) (h:(match Spring.kB with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }) =
match Spring.kC with
| Spring.kA => { id := "spring A" }
| Spring.kC => { id := "spring C" }
| Spring.kB => { id := "spring B" }⊢ ¬"spring B" = "spring C" All goals completed! 🐙)
The quantities written over the objects are one line each. They are the individual
quantities T3 explains, and they are needed now so that the refusals below have something
to refuse; the kinds massK and stiffnessK they name are T2's subject:
def mA : IndividualQuantity Body.A massK Int := ⟨3⟩
def mB : IndividualQuantity Body.B massK Int := ⟨5⟩
def kA : IndividualQuantity Spring.kA stiffnessK Int := ⟨6⟩
def kC : IndividualQuantity Spring.kC stiffnessK Int := ⟨2⟩
def kB : IndividualQuantity Spring.kB stiffnessK Int := ⟨3⟩
What this buys is checked by the compiler: every body is a point mass by rfl, the two
bodies share a sort, and a quantity reads back its two indices as an individual property
that names the kind and the bearer:
/-- Left edge: a substance instantiates its kind (sort). -/
example : Sorted.sortOf Body.A = bodyS := rfl
example : Sorted.sortOf Spring.kC = springS := rfl
theorem bodies_one_sort : Sorted.sortOf Body.A = Sorted.sortOf Body.B := rfl
/-- Bottom + right edges: the two type indices of a mode, read back as an individual
property — the mode characterizes body A and instantiates mass. -/
example : mA.toIndividualProperty = ⟨massK, ⟨"body A"⟩⟩ := rflWhat it refuses is the first value proposition of the object axis. The masses of two bodies do not add, nor the stiffnesses of two springs, and B's mass cannot stand in for A's, each with the error that says why — an error that names the object:
/- Bottom edge: masses of two bodies do not add. -/
/--
error: Application type mismatch: The argument
mB
has type
IndividualQuantity Body.B massK Int
but is expected to have type
IndividualQuantity Body.A massK Int
in the application
IndividualQuantity.add ⋯ mA mB
-/
#guard_msgs in
example := IndividualQuantity.add DifferenceKind.ofScale mA mB
/- Nor stiffnesses of two springs. -/
/--
error: Application type mismatch: The argument
kC
has type
IndividualQuantity Spring.kC stiffnessK Int
but is expected to have type
IndividualQuantity Spring.kA stiffnessK Int
in the application
IndividualQuantity.add ⋯ kA kC
-/
#guard_msgs in
example := IndividualQuantity.add DifferenceKind.ofScale kA kC
/- B's mass cannot stand in for A's. -/
/--
error: Type mismatch
mB
has type
IndividualQuantity Body.B massK Int
but is expected to have type
IndividualQuantity Body.A massK Int
-/
#guard_msgs in
example : IndividualQuantity Body.A massK Int := mB
The case study types the same oscillator with nominal objects, a name such as
⟨"oscillator A"⟩ and no sort. One line shows what that costs. dedicatedFor, T2's
construct, reaches the catalogue entry dedicated to an object through the object's sort,
and an object with no sort has no way up; the first line of the error is the whole story:
/-! The case study's objects are nominal `System`s and know no sort, so they cannot reach
the top edge through `dedicatedFor` at all — the refusal that says why the tutorial needs
an object type with a `Sorted` instance. -/
/--
error: failed to synthesize instance of type class
Sorted System
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example := massK.dedicatedFor (⟨"oscillator A"⟩ : Object) whole
A dimension-and-units library has no object axis at all: mA + mB is a sum of two masses
and nothing more can be said. Tagging the object into the type by hand recovers the refusal
and loses the sum (T0's third row); declaring the object as an index of the quantity keeps
both, as T3 shows.
Reference: The object type for Sorted, Designated, and the
individual quantity; Foundations for the square this part walks.