← All tutorials

Tutorial: finding proven code without trusting a name

Suppose you need a commutative binary operation. On GitHub you'd search text, find a thousand adds, and audit one on faith. Here you ask the commons what it can prove, and get back verified implementations — matched by what they do, not what they're called. This walks through all four ways to ask.

The setup is just the repo's committed store, which already contains rat-add and rat-mul, both proven commutative. Nothing below trusts a name.

1. By example — "who shares a law with this?"

Point at a definition; get every other one that satisfies the same property, matched by the property's content hash.

$ oath find rat-add
  · commutes [proven here]  #f230af55f94f
      rat-mul   (proven as "commutes")  ← proven on both: interchangeable for this law
  · assoc [proven here]  #59b248e21d01
      (no definition in the store satisfies this)

rat-mul came back with no name trusted — only because its commutativity property hashes to the same value as rat-add's. Properties are content-addressed the same way code is: the function is self and the binders are de Bruijn, so "commutativity" has one canonical hash wherever it appears.

2. By a fresh spec — "who proved this?"

You usually don't have an example — you have a spec. Write it as a (defn ...) whose property is the query (self is the sought function; the body is any trivial placeholder):

$ cat commutative.oath
(defn wanted [] [(a Int) (b Int)] Int (+ a b)
  (prop commutative [(a Int) (b Int)] (== (wanted a b) (wanted b a))))

$ oath find --spec commutative.oath
spec query "wanted" — which proven definitions satisfy it (by content hash, no name, no example):
  · commutative [tested here]  #f230af55f94f
      rat-add   (proven as "commutes")  ← a proven implementation of this spec
      rat-mul   (proven as "commutes")  ← a proven implementation of this spec

Note the types: your spec is over Int, the implementations are over Rat, and they still match. Discovery is up to operand types — a law polymorphic in its type matches across the types it ranges over (the numeric tower earned that).

3. The syntactic wall — and proving through it

Content-hashing is fast but syntactic. Write the same law a different way — operands of == flipped — and the hash surface misses it:

$ cat flipped.oath
(defn wanted [] [(a Rat) (b Rat)] Rat (+ a b)
  (prop flipped [(a Rat) (b Rat)] (== (wanted b a) (wanted a b))))

$ oath find --spec flipped.oath
  · flipped [tested here]  #7ab35f3163c4
      (no definition in the store satisfies this)

Nothing — the AST differs, so the hash differs. For the cases the hash surface misses, ask the prover: find --implies appends your spec to each same-signature definition and proves it (via Z3), so it finds anything that provably satisfies the spec, however that definition wrote its own:

$ oath find --implies flipped.oath
spec query "wanted" — which definitions PROVABLY satisfy it (proof-implication, not shape match):
  · flipped
      rat-add   ← provably satisfies it (direct (lemma-free))
      rat-mul   ← provably satisfies it (direct (lemma-free))

Now they're found — Z3 knows addition is commutative over the real numbers, so the flipped statement discharges directly. Syntactic when it can, semantic when it must.

4. The e-graph — "which of these are the same function?"

The deepest question is body-equivalence: two different implementations that compute the same thing. Put a few, deliberately varied:

$ oath put example.oath   # four little sums:
(defn sum-ab [] [(a Int) (b Int)] Int (+ a b))
(defn sum-ba [] [(a Int) (b Int)] Int (+ b a))
(defn sum3-l [] [(a Int) (b Int) (c Int)] Int (+ (+ a b) c))
(defn sum3-r [] [(a Int) (b Int) (c Int)] Int (+ a (+ b c)))

sum-ab and sum-ba have different identities (#6c07… vs #7a82…) — they are genuinely two objects. But they're the same function, and find --equiv says so, by normalizing each body to a canonical form and comparing:

$ oath find --equiv sum-ab
definitions equivalent to sum-ab (#6c0735984f65) — same function up to the rewrite rules, distinct identities:
  eHash 79de5037be8b
      sum-ba   #7a822fd6f5ca

$ oath find --equiv sum3-l          # associativity, too
  eHash 7a63c2aa3e19
      sum3-r   #09741761a5b5

The rules are commutativity and type-directed associativity — and the type-direction is the interesting part. Over Int, (+ (+ a b) c) and (+ a (+ b c)) collapse. Over Float they would not, because float addition isn't associative — the very law examples/float.oath falsifies. The e-graph only applies a rewrite where it's sound, and it learned where that is straight from the numeric tower.

The two things to take away

No name was ever trusted. Every match above is by content hash, by proof, or by canonical form — never by a label anyone can repoint. That's what makes a shared, deduplicated commons possible instead of ten thousand siloed copies.

Identity was never touched. sum-ab and sum-ba stay two distinct objects with two distinct hashes; the e-graph draws an equivalence edge over the hash graph, it never merges them. Discovery is a view over the substrate, never a change to it — which is exactly why it can keep getting smarter (deeper rules, more of the ladder) without ever forking reality. See docs/discovery.md and docs/egraph.md.