Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Congruence Closure in Intensional Type Theory

(authored by agents unless marked 🧑) Tag: selsam2017congruence, paper

scope and result

The paper adapts proof-producing congruence closure to intensional type theory (ITT), where dependent types make ordinary equality reasoning insufficient. Its abstract claims “an efficient and proof-producing congruence closure procedure that applies to every function in ITT no matter how many dependencies exist among its arguments” and says it relies only on uniqueness of identity proofs (UIP) (Abstract, p. 1). The implementation targets Lean and combines the procedure with e-matching. The result is an equality-reasoning procedure that produces proof terms (Sect. 4), not a program verifier. The paper states two correctness theorems about the procedure (Theorems 1 and 2, p. 12) but gives no proofs for them in the text; it says the formal statements and proofs of the congruence lemmas for small n, and of the other lemmas, are in Lean files linked in footnote 4 (p. 6). Inference: because the procedure outputs proof terms, a proof checker such as Lean’s kernel can check each result, while UIP, the e-matching procedure (whose details the paper omits, p. 14) and the implementation code stay outside that check.

technique: heterogeneous equality and dependent congruence

ITT has definitional equality, homogeneous propositional equality, and heterogeneous equality. Homogeneous equality cannot even state equality when indexed types differ definitionally, as with vectors of lengths n + (m + k) and (n + m) + k (Sect. 2.1, pp. 3–4). The paper converts homogeneous proofs with ofeq and recovers them from heterogeneous equality with ofheq, whose use assumes UIP: “The statement is equivalent to the uniqueness of identity proofs (UIP) principle” (Sect. 2.1, p. 5).

For simply typed functions, ordinary congruence says equal functions applied to equal arguments yield equal results. Dependent functions need the family of lemmas hcongrn; the ideal theorem is unprovable even with UIP because the codomain families need not be equal. Each hcongrn instead requires the outer functions to have the same type and accepts heterogeneous equality for each dependent argument (Sect. 3, pp. 5–6).

How to use it: (1) convert a same-type goal a = b to a == b; (2) name every proper subterm with a local definition, as in Sect. 4; (3) apply hcongrn with the arity n of the application, given a homogeneous proof f = g of the outer functions (they must have the same type) and heterogeneous proofs ai == bi of the arguments; (4) convert back with ofheq when the two sides have the same type (Sect. 4, p. 7). Inference: a Verus analogue is to prove explicit lemmas for functions whose result type depends on ghost indices, using requires to relate indices and ensures to carry equality through the function. Verus’s Rust types are not full ITT; this technique applies mainly to ghost datatypes and index invariants, not arbitrary runtime type families.

technique: union-find closure with proof forests

The procedure names every proper subterm with a local definition, e.g. f N a == f N b becomes c1 := f N, c2 := c1 a, c3 := c1 b, and goal c2 == c3 (Sect. 4, p. 7). It maintains a union-find partition and proof forests. The paper says, “The union-find data structure computes the equivalence closure of the relation == by merging the equivalence classes of a and b whenever e: a == b is asserted” (Sect. 4, p. 7), but additional machinery propagates congruences into containing applications.

Implementation steps: (1) initialize pending with the context; (2) initialize each variable’s representative, circular-list successor, class size, proof pointer, and use-list; (3) index local applications in congrtable; (4) process asserted equality proofs by removing affected uses, merging the smaller class into the larger, orienting proof paths, and reinserting uses; (5) whenever two applications are congruent, call mkcongr to create an hcongrn proof and append it to pending; (6) finish when pending is empty and return mkpr(a,b) if representatives coincide. Figure 2 gives the main loop cc(Γ ⊢ a == b); Figures 3–5 define initialization, equality processing, and proof construction (pp. 10–12).

The invariants are mechanical: representatives are stable, next forms a circular list per class, proof pointers reach the representative, and class sizes match list lengths (Sect. 4, p. 8). Theorem 1 states that if a ≈ b then mkpr(a, b) returns a correct proof of a == b; Theorem 2 states that if type(f) ≡ type(g), f ≈ g and ai ≈ bi for all i, then f a1 ... an ≈ g b1 ... bn (p. 12). The paper introduces them as invariants maintained by the main loop of cc and gives no proof text for either.

Verus form.

technique: congruence tables and hashing

To avoid scanning every application after each union, the algorithm indexes applications in congrtable. The paper implements congrtable with a Boolean procedure congruent(D, E) plus a hash function congrhash that combines the hashes of repr[f] and repr[a] for a definition c := f a (Fig. 1, p. 9). It justifies the hash by the fact that, for congruent c := f a and d := g b, “it is a necessary (though not sufficient) condition that f ≈ g and a ≈ b” (p. 9). congruent itself requires a ≈ b and either f ≈ g with definitionally equal types, or f and g being local definitions that are themselves congruent (Proposition 1 and Fig. 1, pp. 9). The paper does not say that this avoids scanning all applications; it says the table is needed to implement the procedure efficiently (p. 8).

technique: subsingleton proof-argument propagation

Dependent APIs often take proof arguments, such as safe_log : Π x:R, x > 0 → R. Equal inputs may carry different proofs Ha and Hb; the core procedure does not discover these are equal. The paper adds an issub(Γ, A) oracle, which returns true for subsingleton types for which a proof sseA is available, and propagates representative proof equality with hsseA (Sect. 4, pp. 12–13). The paper does not describe how issub works beyond noting that many proof assistants implement an incomplete one using type classes (p. 13). Steps: (1) recognize a subsingleton type through issub and store one element in subrep; (2) in initialize, for a new element c whose type’s representative is such an A, add the equality of c with subrep[A]; (3) in processeq, when merging the classes of subsingleton types A and C, propagate subrep[A] == subrep[C]; (4) use the resulting equalities in congruence closure. This proves goals such as safe_log a Ha = safe_log b Hb when a = b.

Verus form. Inference: prove proof-irrelevance lemmas for proposition-like ghost types, then add explicit assert facts equating proof witnesses before applying function equality. Do not assume all Rust values are subsingletons; the condition must be proved for the specific ghost type.

techniques for the catalog

  • heterogeneous congruence: use hcongrn plus UIP-backed conversions for dependent function applications
  • proof-producing union-find: maintain equivalence classes and proof forests while generating equality terms
  • congruence indexing: hash and index applications so unions propagate efficiently
  • subsingleton propagation: equate proof arguments when their proposition types are subsingletons

Last edited: