Typed heaps over identifier sets #
Heap Ident (with [CellSpec Ident]) is a dependent function
(i : Ident) → CellSpec.type i. It models package state as a collection of
named, typed cells indexed by an identifier set Ident.
Composition uses disjoint sums of identifier sets. A heap over α ⊕ β splits
canonically into a heap over α and a heap over β, and cell framing follows
from ordinary function update: reading cell j after writing cell i ≠ j
returns the old value.
Usage #
inductive MyCells
| counter
| flag
deriving DecidableEq
instance : CellSpec MyCells where
type | .counter => Nat | .flag => Bool
default | .counter => 0 | .flag => false
def initial : Heap MyCells := Heap.empty
def stepped : Heap MyCells := initial.update .counter 5
example : stepped.get .counter = 5 := by simp [stepped]
example : stepped.get .flag = false := by simp [stepped]
Stateful handlers can use this layer as a typed cell-keyed state convention:
Heap Ident stores one value of the cell-specific type at every identifier.
CellSpec : a typed cell directory #
A CellSpec Ident says: each identifier i : Ident carries a value type
CellSpec.type i : Type v, with a designated CellSpec.default i. The
default is what the heap holds before any cell is written.
Ident lives in Type u, value types in Type v; both universes are
independent. Sum composition (Sum.instCellSpec below) requires both
operands' universes to match.
Heap : the dependent-function heap #
Heap Ident is the type of states: one cell-value per identifier. Lives in
Type max u v.
Write v : CellSpec.type i to cell i, leaving all other cells
unchanged.
Instances For
Sum of identifier sets : CellSpec instance #
Composing two packages with identifier sets α and β yields one with
identifier set α ⊕ β. The cell directory is the disjoint union of the two,
defaults too.
PEmpty : CellSpec instance for the empty identifier set #
The trivial heap Heap PEmpty has a unique inhabitant (the empty function).
It is useful whenever a typed-heap construction needs a stateless identifier
set.
Heap.split : the canonical decomposition #
Heap (α ⊕ β) ≃ Heap α × Heap β, the lynchpin of state-separating
composition in this layer. The forward direction restricts to inl / inr
cells; the inverse is Sum.rec on the identifier. Both directions reduce by
case-analysis on the identifier and are rfl-definitional after cases.
Splitting after an update on the left identifier set updates only the left split component.
Splitting after an update on the right identifier set updates only the right split component.
Rebuilding a split heap after updating the left component is the same as
updating the composite heap at the corresponding Sum.inl cell.
Rebuilding a split heap after updating the right component is the same as
updating the composite heap at the corresponding Sum.inr cell.
Sanity check: a two-cell heap #
A small example demonstrating cell access, frame, and the canonical
Sum-split. If you're reading this to learn the API, this is the right
starting point.