Sorting term lists by the verified total order #
canon sorts the leaves of an AC operator using the LinearOrder AST from MeTTaILProofs.Order. The
only facts we need about the sort are that it permutes its input, that its output is sorted, and the
consequence that permutation-equal inputs sort to the same list (sorted permutations are unique).
Sort a list of terms by the verified total order on AST.
Equations
- MeTTaIL.AC.sortMS xs = xs.mergeSort fun (x1 x2 : MeTTaIL.AST) => decide (x1 ≤ x2)
Instances For
Permutation-equal lists sort to the same list. This is the heart of completeness: it turns "same multiset of leaves" into "syntactically identical canonical form", using that two sorted permutations of each other are equal.
Flatten and rebuild for a single AC label #
flat l t collects the leaves of the binary l-tree at the root of t: it descends through nodes
(l a b) and stops at anything that is not such a node. rebuild l xs turns a leaf list back into a
right-nested l-tree. These are the two halves of the canonical form for one associative-commutative
label l. flat does not recurse with canon, so it is plain structural recursion on the term.
The leaves of the binary l-tree at the root of a term. Descends through (l a b) nodes; every
other shape (including the same label at a different arity) is an opaque leaf.
Equations
- MeTTaIL.AC.flat l (MeTTaIL.AST.sexp m [a, b]) = if l = m then MeTTaIL.AC.flat l a ++ MeTTaIL.AC.flat l b else [MeTTaIL.AST.sexp m [a, b]]
- MeTTaIL.AC.flat l (MeTTaIL.AST.var p) = [MeTTaIL.AST.var p]
- MeTTaIL.AC.flat l (b.subst r v) = [b.subst r v]
- MeTTaIL.AC.flat l (MeTTaIL.AST.sexp m args) = [MeTTaIL.AST.sexp m args]
Instances For
Rebuild a leaf list into a right-nested l-tree. A single leaf rebuilds to itself; the empty list
is a degenerate case that never arises from flat (which is always nonempty).
Equations
- MeTTaIL.AC.rebuild l [] = MeTTaIL.AST.sexp l []
- MeTTaIL.AC.rebuild l [x_1] = x_1
- MeTTaIL.AC.rebuild l (x_1 :: xs) = MeTTaIL.AST.sexp l [x_1, MeTTaIL.AC.rebuild l xs]
Instances For
Rebuilding an l-flat, nonempty leaf list and flattening it again returns the same list. The leaf
list produced inside canon is exactly such a list, so flattening a canonical AC subterm recovers
its sorted leaves.
The AC congruence #
ACEq acOp is the smallest congruence on terms generated by commutativity and associativity of the
binary applications (l a b) whose label l is flagged acOp l = true. The generators (comm,
assoc) fire only on binary nodes, matching a binary-curried operator like rho's |; a flagged label at
another arity is inert. The congruence rules (argCong, substCongB, substCongR) close it under all
term contexts; refl, symm, trans make it an equivalence.
AC-equivalence: the congruence generated by commutativity and associativity of the binary
applications of the acOp-flagged labels.
- refl {acOp : Label → Bool} (t : AST) : ACEq acOp t t
- symm {acOp : Label → Bool} {s t : AST} : ACEq acOp s t → ACEq acOp t s
- trans {acOp : Label → Bool} {s t u : AST} : ACEq acOp s t → ACEq acOp t u → ACEq acOp s u
- comm {acOp : Label → Bool} {l : Label} {a b : AST} : acOp l = true → ACEq acOp (AST.sexp l [a, b]) (AST.sexp l [b, a])
- assoc {acOp : Label → Bool} {l : Label} {a b c : AST} : acOp l = true → ACEq acOp (AST.sexp l [AST.sexp l [a, b], c]) (AST.sexp l [a, AST.sexp l [b, c]])
- argCong {acOp : Label → Bool} {l : Label} (pre : List AST) {a a' : AST} (post : List AST) : ACEq acOp a a' → ACEq acOp (AST.sexp l (pre ++ a :: post)) (AST.sexp l (pre ++ a' :: post))
- substCongB {acOp : Label → Bool} {b b' r : AST} {v : DottedPath} : ACEq acOp b b' → ACEq acOp (b.subst r v) (b'.subst r v)
- substCongR {acOp : Label → Bool} {b r r' : AST} {v : DottedPath} : ACEq acOp r r' → ACEq acOp (b.subst r v) (b.subst r' v)
Instances For
Congruence of sexp under a pointwise AC-equivalence of arguments, built by chaining single-position
argCong steps across the list.
Soundness lemmas: flatten, rebuild, and sort preserve AC-equivalence #
The canonical form #
The canonical rebuild of one node, given its already-canonical arguments. For an acOp label with
exactly two arguments this flattens, sorts, and rebuilds; otherwise it is an ordinary application.
Equations
- MeTTaIL.AC.canonNode acOp l [a, b] = if acOp l = true then MeTTaIL.AC.rebuild l (MeTTaIL.AC.sortMS (MeTTaIL.AC.flat l a ++ MeTTaIL.AC.flat l b)) else MeTTaIL.AST.sexp l [a, b]
- MeTTaIL.AC.canonNode acOp l cargs = if acOp l = true then MeTTaIL.AST.sexp l cargs else MeTTaIL.AST.sexp l cargs
Instances For
The canonical form of a term: canonicalize the arguments, then canonNode each application.
Equations
- MeTTaIL.AC.canon acOp (MeTTaIL.AST.var p) = MeTTaIL.AST.var p
- MeTTaIL.AC.canon acOp (b.subst r v) = (MeTTaIL.AC.canon acOp b).subst (MeTTaIL.AC.canon acOp r) v
- MeTTaIL.AC.canon acOp (MeTTaIL.AST.sexp l args) = MeTTaIL.AC.canonNode acOp l (MeTTaIL.AC.canonList acOp args)
Instances For
Pointwise canon of an argument list.
Equations
- MeTTaIL.AC.canonList acOp [] = []
- MeTTaIL.AC.canonList acOp (a :: as) = MeTTaIL.AC.canon acOp a :: MeTTaIL.AC.canonList acOp as
Instances For
Soundness: the canonical form is AC-equivalent to the original #
The canonical rebuild of one AC node is AC-equivalent to the binary application of its two canonical arguments: sorting permutes the flattened leaves (commutativity) and rebuild-of-flatten re-associates them (associativity).
Pointwise soundness for argument lists.
Completeness: the canonical form is invariant under AC-equivalence #
Completeness of the canonical form: AC-equivalent terms have identical canonical forms. Proved by
induction on the AC-equivalence derivation. The comm case reduces to permutation-invariance of the
sort; the assoc case to associativity of append under the sort; the congruence cases to the
pointwise canonicalization of arguments.
The canonical form is idempotent: it is a genuine normal form for AC-equivalence. Immediate from
soundness (canon t is AC-equivalent to t) and completeness (AC-equivalent terms canonicalize
alike).
The decision procedure #
AC-equivalence is decided by equality of canonical forms. Soundness gives the reverse direction: if the canonical forms are equal then both terms are AC-equivalent to that common form.
AC-equivalence is decidable: compute and compare canonical forms.
Equations
- MeTTaIL.AC.instDecidableACEq acOp s t = decidable_of_iff (MeTTaIL.AC.canon acOp s = MeTTaIL.AC.canon acOp t) ⋯