050. Narrow Local unique() comparisons with fingerprints

  • Status: Implemented
  • Date: 2026-10-14

Problem and mechanism

Local list:unique() previously compared each item with every earlier item. It now preserves the short-list paths, then fingerprints each item and compares it only with prior items in the same fingerprint bucket. Fingerprints are candidate selectors, never proof of equality: each candidate is confirmed by the existing structural HFF equality operation. The helper is shared with Local distinct().

The scan remains incremental. Empty and singleton lists return true, two-item lists use the existing direct comparison, and an equal first pair returns false before hashing or reading later elements. For larger lists, the implementation fingerprints and checks each element in order, returning false as soon as an equal prior candidate is found. Cancellation and MaxSteps accounting apply during fingerprint construction and collision checks. The boolean operation creates no generated collection result and adds no MaxGeneratedValues charge.

Applicability

Execution profile Status Reason
Local list:unique() Applied Fingerprints narrow candidate comparisons; HFF equality verifies each candidate.
Local list:distinct() Existing mechanism It already uses the same fingerprint buckets and collision-confirmed equality.
Other backends Not changed Their equality and uniqueness lowering is unaffected.

Reproduction

The baseline is 9731d3e. The candidate is the commit containing this record and the implementation. Go 1.27.1 was used. The deterministic work ledger does not depend on hardware and is not a timing measurement.

Run the Local-focused candidate tests and full checks:

go test ./backends/local -run 'TestUnique|TestFingerprintCollisionRequiresEquality' -count=1 -v
go test ./...
go vet ./...

backends/local/unique_kernel_test.go compares the fingerprint path with a pairwise HFF-equality oracle over 1,296 value pairs and 300 deterministic random list matrices. It also forces collisions, checks numeric precision boundaries, and records isolated work counts. backends/local/unique_test.go covers the public result, early duplicates, a late mixed-numeric duplicate, work limits, and cancellation. The same 14 public semantic cases and cancellation checks were run on the pinned baseline using a copied public test fixture; raw logs are retained at /private/tmp/hff-unique-fingerprint-*. Its pairwise work exceeds the candidate's 1,000-step limit for 256 distinct integers, while the fingerprint path passes with MaxSteps: 1000 and MaxGeneratedValues: 1.

Results

The isolated kernel test counted charged evaluation steps for lists of distinct Int values. The candidate retains the same fast-path counts for zero, one, or two values; larger lists visit each value once after the initial pair comparison:

Distinct Int count Pairwise oracle steps Fingerprint steps
0 0 0
1 0 0
2 1 1
16 120 17
128 8,128 129
512 130,816 513

These are isolated algorithm-ledger counts from the unit test. They do not include whole-match validation or the other query-evaluation charges, and they are not runtime measurements. The implementation uses auxiliary bucket and index storage. Hash collisions can still require many equality checks, so worst-case linear work is not guaranteed; those checks remain cancellable and budgeted.

Correctness and tradeoffs

The fingerprint must assign equal values to the same bucket. It normalizes equal Int and Float numeric values without rounding large Ints through Float; recursive List values preserve element order, while Objects use their actual key sets in deterministic key order. Nulls, tagged scalar values, nested containers, and numeric boundaries remain subject to the same HFF equality confirmation as before. Unequal values may share a bucket without changing the result because e.equal resolves every collision.

The input is processed in order and the first duplicate still returns false without examining the remainder. Fingerprint traversal and equality both charge MaxSteps and observe context cancellation. unique() remains a boolean-only operation and does not charge generated output values. The candidate map adds temporary memory proportional to indexed values and collision buckets.

Decision and limits

Use deterministic fingerprints to narrow Local unique() candidate checks, with HFF equality as the final authority. Keep the small-list fast paths, streaming early exit, cancellation, and work-budget checks. The work-count results describe the isolated kernel only; they do not establish runtime speed or a linear worst-case bound.

Explore HFF
Search documentation

Search the language specification and guides.