Add str/prn for Diff, refactor unified, add unified-with-context#9
Conversation
There was a problem hiding this comment.
Build & Tests
CI: pass (ubuntu-latest, macos-latest) — all 62 tests pass (54 existing + 8 new).
Local build: no Carp compiler available on this machine — relied on CI.
Findings
Reviewed all changed code in diff.carp (lines 14-266) and all 8 new tests in tests/diff.carp (lines 313-381).
str/prn (lines 14-26): correct
Both use match-ref (borrowing), dispatch to the inner type's str/prn, and wrap with the variant name. Standard Carp pattern for sumtype printing. implements declarations are present.
format-hunk rename (lines 144-159): correct
Renamed from emit-hunk, now returns a single hunk string instead of concatenating onto the accumulator. Clean separation of concerns.
change? helper (line 142): correct
Returns false for Eq, true for Insertion/Deletion. Used to deduplicate the hunk-start logic.
unified-with-context refactoring (lines 165-261): correct
Verified the refactoring preserves behavior by tracing through several cases:
-
Hunk-start deduplication: the
when-do (and (not in-hunk) (change? elem))block (lines 181-189) runs before thematch-ref, correctly replacing the identicalunless-do in-hunkblocks that were duplicated in the oldDeletionandInsertionbranches. Sincechange?returns false forEq, thewhen-donever fires for context lines. ✓ -
O(n²) elimination: result accumulation changed from
(String.concat &[output header ... body ...])(copying the entire string on each hunk emission) to collecting into ahunksarray and concatenating once at the end (line 261). Genuine asymptotic improvement for multi-hunk diffs. ✓ -
ctx=0: traced through manually —
trailing = min(0, n) = 0andleading = min(0, ...) = 0correctly suppress all context lines. The test at line 332 confirms. ✓ -
ctx=1 multi-hunk: traced through — two hunks with 3 intervening Eq lines (gap > 2*ctx) correctly split into separate hunks. The test at line 359 confirms. ✓
-
Edge case — change at start of diff:
lead-linesis empty initially, sohunk-linesstarts empty,hunk-old-count = 0. Context is only appended after the change. ✓ -
Edge case — change at end of diff: the post-loop
when in-hunk(line 254) correctly emits the final hunk. ✓
unified delegation (line 266): correct
(defn unified [d] (unified-with-context d 3)) — matches the original hardcoded context of 3. The test at line 350 explicitly checks unified and unified-with-context 3 produce identical output.
Minor notes (non-blocking)
- No test for
prnoutput (onlystris tested). For integer arrays they produce the same output, so this is cosmetic. Would be meaningful for(Diff String)whereprnquotes strings. - No CHANGELOG in this repo.
Verdict: merge
Clean, well-motivated refactoring. The O(n²) fix is genuine. Hunk-start deduplication reduces 15 lines of copy-paste to a single when-do block. unified-with-context is a useful addition. All 62 tests pass. No bugs found.
Summary
str/prnfor theDifftype: theDiffsumtype now implements both interfaces, so diffs can be printed and formatted (e.g.(str &(Eq [1 2 3]))→"(Eq [1 2 3])")unified: eliminated O(n²) string accumulation (now collects hunk strings in an array and concatenates once at the end) and deduplicated the hunk-start logic that was copy-pasted across theDeletionandInsertionbranches (now a singlewhen-doblock before thematch-ref)unified-with-context: new public function that takes the number of context lines as a parameter;unifiednow delegates to it with 3. Renamed the internalemit-hunk(which accumulated into the result string) toformat-hunk(which returns a single hunk string)stron all three variants,unified-with-contextwith 0/1/3 context lines, and equivalence withunifiedAll 62 tests pass.
carp-fmtandanglerclean.Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.