Skip to content
Find a name reused across scopes

Find a name reused across scopes

Find where repeated content crosses from one bracket depth to another, such as a name bound outside a block and used inside it, and compare terms up to the names they bind (relation).

$ cat totals.js
let total = 0;
for (const item of items) {
  if (item.ok) { total = total + item.size; }
}
report(total);

Where a reuse crosses a scope

A reuse joins a later occurrence of repeated content to the earlier one. Its residual is how many brackets deeper the later occurrence is, and the holonomy sums the residuals without their sign, 0 for text whose every reuse stays at one depth:

$ trex relation totals.js --edges
trex relation: 106 bytes, 56 tokens, max depth 2, nesting load 34
... the readings and every edge ...
  reuse chords (from -> to, residual = scope jump):
          item -> item       residual +1
         total -> total      residual +2
         total -> total      residual +0
          item -> item       residual +0
         total -> total      residual -1

total is declared at depth 0 and first used two brackets in, inside the for block and the if block, the reuse of residual +2; the loop variable item reaches one bracket deeper. The net holonomy keeps the signs, so +2 says the reuse flows inward on the whole: the final report(total) comes back out by one.

Terms equal up to the names they bind

The first word inside a [ binds a name for the rest of the group. The canonical form writes that binder as # and each later use of it as ^k, the binder k groups out, so two terms that differ only in the names they bind read alike, and a name used in its binder’s place stands out:

$ trex relation --canonical --text 'map [item (item.size)] items'
... the readings ...
  canonical form (alpha-equivalence):
    map [ # ( ^0 . size ) ] items

$ trex relation --canonical --text 'map [row (row.size)] items'
... the readings ...
  canonical form (alpha-equivalence):
    map [ # ( ^0 . size ) ] items

$ trex relation --canonical --text 'map [row (item.size)] items'
... the readings ...
  canonical form (alpha-equivalence):
    map [ # ( item . size ) ] items

The third term binds row and never uses it, reading the outer item instead: its canonical form keeps item as a name bound nowhere, where the other two read the binder.