Follow the interface. Choose the representation. Compose the language.
Use fifteen programs to reconnect abstraction barriers, recursive data, identity, sequence stages, symbolic constructors, tagged dispatch, bounds and ordering, coding trees, multiple representations, coercion, sparse algebra, painters, frames, and recursive picture composition.
Can you predict a data program while keeping its abstract interface, structural shape, identity, representation, declared conversions, and geometric frame separate?
- Keep representation knowledge behind constructors and selectors
- Follow a sequence one cdr at a time
- Follow both branches of a tree without assuming its depth
- Distinguish one shared object from two equal objects
- Compose enumeration, filtering, mapping, and accumulation
- Transform symbolic expression data through simplifying constructors
- Dispatch one generic operation through a data type tag
- Propagate lower and upper bounds through an interval interface
- Stop ordered-set membership after passing the target
- Use one code-tree representation to encode and decode branch paths
- Choose rectangular or polar contents without changing the complex-number interface
- Move mixed numeric values through explicit coercion or raising paths
- Add and multiply sparse polynomials through generic tagged methods
- Map one unit-square painter through square or skewed frames
- Compose transformed painters and produce a finite recursive segment set
The rational, interval, and complex-number programs keep concrete contents behind constructors and selectors. Their clients ask for abstract components rather than reaching into storage directly.
Sequence and tree procedures follow the data definition. A sequence leaves one smaller cdr; a tree pair leaves two structural branches. Shared identity adds a different question: whether two paths reach one allocation or two equal allocations.
The pipeline names enumeration, filtering, mapping, and accumulation as separate transformations. The symbolic differentiator similarly separates recursive mathematical rules from representation cleanup in make-sum and make-product.
The operation-table example retrieves representation-specific procedures through operation and type keys. Interval arithmetic exploits bound selectors, while the ordered-set program exploits increasing order to skip a tail that cannot contain the target.
The Huffman program uses one tree in both directions. The complex-number program shows how one abstract value can retain two representations and let each operation choose convenient contents.
The mixed-type program keeps conversion rules separate from arithmetic methods. The polynomial package carries a variable and descending sparse terms, combines equal powers, removes zero coefficients, and rejects mismatched variables before manipulating contents.
The picture-language programs reuse the same abstraction pattern geometrically. A painter maps unit-square segments through a frame supplied later. Transform combinators create subframes and return new painters; only the final paint call produces SEG output. The SVG is parsed from those Lispex transcript lines rather than redrawn from a separate TypeScript model.
(begin
(define (make-rat n d) (cons n d))
(define (numer x) (car x))
(define (denom x) (cdr x))
(define (add-rat x y)
(make-rat (+ (* (numer x) (denom y))
(* (numer y) (denom x)))
(* (denom x) (denom y))))
(let ((answer (add-rat (make-rat 1 2) (make-rat 1 3))))
(/ (numer answer) (denom answer))))- Output
- —
- Value
- —
- Diagnostic
- —
The fifteen programs return 5/6, (4 (a b c d)), (10 (20 (30 40) 50) (60 70)), (#0=(changed) . #0#), 84, (+ x (+ x 3)), (5.0 5.0), (4 7), (#f 3), ((0 1 1 1 1 0) (A D B)), (rectangular polar #t #t #t #t), ((rational 7 2) ((integer rational))), ((polynomial x (2 2) (1 2)) different-variables), (segments 6), and (segments 12) in that order. The last two also print SEG lines rendered by the picture visualizer.
Locate constructor and selector calls, shrinking sequence and tree recursion, shared allocation and mutation, pipeline intermediate lists, derivative constructors, operation-table keys, bound selection, ordered early exit, Huffman branch choices, complex representation dispatch, coercion retry, polynomial term merging, frame-coordinate mapping, subframe construction, and final SEG emission. These bounded traces describe only the selected runs.
Fifteen checks for reasoning about data systems
Why can rational operations survive a storage change?
Answer They obtain components through numer and denom and create values through make-rat, so storage order remains behind the interface.
Why do sequence and tree procedures branch differently?
Answer A sequence convention leaves one smaller sequence in cdr, while a tree pair contains two structural branches that both require work.
Why does one mutation appear through both sides of a shared tree?
Answer Both paths contain references to the same allocated pair rather than copies with merely equal contents.
What boundary separates sequence-pipeline stages?
Answer Each stage receives and returns the sequence shape required by its neighbor, so its internal recursion does not leak into the other stages.
Why do derivative constructors own simplification?
Answer They centralize representation rules for sums and products, leaving deriv to state recursive differentiation rules without duplicating zero and one cases.
Why does apply-generic not branch at every caller?
Answer It combines the operation name and data tag to retrieve a representation-specific procedure, then gives that procedure only the untagged contents.
Why can interval addition ignore pair layout?
Answer It reads components through lower-bound and upper-bound and constructs the result through make-interval.
Why can ordered membership stop after passing the target?
Answer The increasing-order invariant guarantees that every unvisited element is still larger, so no remaining element can match.
Why must Huffman decoding return to the root after a leaf?
Answer A leaf completes one variable-length code word; the next unread bit begins the next symbol from the same root.
Why do complex addition and multiplication prefer different representations?
Answer Addition combines real and imaginary parts directly, while multiplication combines magnitudes and angles directly. The shared interface lets each operation choose convenient contents.
Why are coercion rules kept separate from arithmetic methods?
Answer A conversion changes representation, while a method performs the requested operation once compatible contents have been selected. Keeping them separate makes the retry path explicit.
Why does the sparse polynomial package remove zero coefficients?
Answer Omitting zero terms preserves a canonical sparse representation, so cancellation does not leave meaningless powers in later merges and products.
How does a painter stay independent of final page coordinates?
Answer Its segments use unit-square coordinates. Only painter application receives a frame and maps those relative endpoints into concrete coordinates.
Why does recursive picture composition build painters before it builds segments?
Answer The combinators return procedures that describe how subframes are arranged. A finite segment list is produced only when the resulting painter is finally applied to a concrete frame.
What remains outside this checkpoint?
Answer These observations do not prove behavior for every input and do not create authentication, evidence, or authority.