sicp.io
Chapter 3 · Checkpoint

Track the location. Name the schedule. Demand only what you need.

Use twenty-one canonical programs to reconnect local state, assignment tradeoffs, environment rules, mutable links, circuit time, concurrency control, delayed computation, numerical streams, and functional versus object-oriented histories.

Guiding question

Can you identify which location changes, which frame supplies a name, which schedule creates an observation, which delayed work has been forced, and which interface keeps those details private?

  • Follow assignment across calls to one stateful closure
  • Distinguish private locations created by separate closures
  • Explain how assignment can hide instrumentation or experiment progress
  • Explain why assignment introduces history and order dependence
  • Apply explicit environment lookup, procedure creation, definition, and assignment rules
  • Record one parameter frame per procedure application
  • Separate the first force from a memoized force
  • Demand only a finite prefix of an infinite stream
  • Track front, rear, and link mutations in a queue
  • Track destructive append, aliases, cycles, and unique pair identities
  • Reproduce the same state transitions from one seed
  • Propagate a missing value through connected constraints
  • Order events by explicit simulated time
  • Compose wires, gates, delays, and an agenda into a circuit
  • Locate a lost update caused by stale reads
  • Serialize two state changes through one shared lock
  • Distinguish mutual exclusion from circular-wait avoidance
  • Resolve mutually recursive helpers inside one private invocation environment
  • Update existing table records and link new nested records
  • Represent successive numerical approximations as delayed streams
  • Compare hidden object state with explicit reusable stream histories

Local state, monitored procedures, and finite experiments demonstrate the modular benefit of assignment: callers use one stable protocol while bookkeeping persists privately. Repeated withdrawals and explicit call schedules then show the cost: the same source expression can depend on prior history and order.

The environment evaluator and frame transcript separate lexical lookup from dynamic application. A lambda captures its creation environment; an application adds a new parameter frame. Definition changes the first frame, while assignment searches for and mutates an existing binding.

Queues, mutable tables, destructive append, and cycles change selected links rather than rebuilding whole values. Identity therefore becomes observable, and safe traversal may need an explicit set of already visited pair objects.

Seeded generators, constraint networks, agendas, and digital circuits make hidden context explicit as state, known values, or simulated time. A circuit emerges from local wire actions and delayed gate updates rather than one central truth-table procedure.

Interleaving and serializers expose stale reads and one protected schedule. The two-lock lesson adds another boundary: mutual exclusion permits the displayed circular wait, while a shared id order removes that modeled cycle.

Promises and streams separate a current value from delayed future work. Numerical streams retain every partial sum or Newton guess. Comparing one stateful generator with one functional stream shows equal finite values while preserving different identities, replay behavior, and histories.

SICP code384 of 1,048,576 UTF-8 bytes
Examples
Result
Output
Value
Diagnostic
Execution trace0 / 0 events
    Programs run in the browser with their result and execution trace.
    Expected result

    The original twelve programs retain their documented first observations. Assignment benefits return (9 16 2 reset 0); assignment costs return (90 80 90 90). The environment evaluator returns (7 13), and the application-frame program returns 25 with three frame records. Destructive append returns ((a b c d) (c d) #t). The inverter returns (2 1 4 0), the lock-order model returns ((90 50) (45 95) 95 45 #f #f), numerical streams return exact partial sums and Newton guesses, and the object/stream recurrence produces the same five-number prefix in both models.

    Trace focus

    Find persistent bindings, assignment transitions, explicit schedule order, frame extension and lookup, first versus memoized force, demanded stream tails, queue and table links, shared pair identities, seed updates, connector notifications, sorted agenda insertion, gate actions, stale reads, lock acquisition and release, recursive local helpers, exact partial-sum state, and object versus stream recurrence steps. Each trace records the selected finite run under the declared runtime limits.

    Review

    Twenty-one checks for state, time, identity, and delayed work

    What changes when an answer depends on the calls that came before it?

    Answer Three nested single-binding let expressions make the sequence explicit. Each result therefore reflects the state left by the preceding call instead of starting again from 100.

    Why do two counters made by one procedure not overwrite each other?

    Answer The second example moves value outside both procedures. That single location is then shared, so an update through either procedure becomes the starting point for the other.

    How can two force calls cause only one evaluation?

    Answer The second force returns the stored value without running the body again. The final calls value remains 1, making memoization visible in the result.

    How can a finite run use a sequence with no final element?

    Answer stream-ref forces exactly as many tails as it needs to reach the requested index. Asking for index 9 constructs a finite prefix and returns 10, so this particular run terminates.

    Why does constant local insertion need a rear pointer as well as a front pointer?

    Answer Deletion does not rewrite the links. It advances front to its current cdr. After inserting a, b, and c and deleting once, front names the b pair while rear still names the c pair.

    How can a stateful generator change every call yet repeat exactly?

    Answer The recurrence is deterministic. Two separately constructed generators begin with private locations, but equal starting seeds make their value sequences equal. This lesson generator makes state and reproducibility directly observable.

    How can one relation react correctly no matter which two values arrive first?

    Answer forget-value! succeeds only when the retractor is the stored informant. When the user forgets total, the adder retracts right because it supplied that derived value, but it cannot retract the independently supplied left value. Supplying a new right value then derives a new total through the same relation.

    How can a simulation determine what happens next without following scheduling order?

    Answer propagate removes the earliest event, applies its numeric change to signal, and records the resulting state beside that event time. The agenda models logical simulation time rather than waiting for a wall clock, and these examples do not model simultaneous-event physics beyond their explicit insertion rule.

    What disappears when two updates read the same shared balance before either finishes?

    Answer The second schedule lets the deposit read and write before the withdrawal reads. The withdrawal therefore sees 110 and writes 90. These programs enumerate two selected finite schedules and their exact final balances.

    What must be protected so that an update cannot read state and then finish after another update has changed it?

    Answer make-serializer accepts a shared lock and returns a procedure wrapper. The wrapper acquires before calling the state-changing procedure and clears after receiving its result. In the shipped finite schedule, the serialized deposit finishes before the withdrawal reads balance, so both updates remain in the final value 90. The lesson records this exact schedule and final state.

    How can several local procedures refer to one another without leaking their names into the surrounding environment?

    Answer The second program treats definitions as quoted data and builds the scan-out shape used to reason about simultaneous local scope: create every name with an explicit unassigned marker first, then install each procedure with set!, then evaluate the remaining body. This run constructs the transformation as data and makes its unassigned and set! structure explicit.

    Which links must change when a table updates one record or creates a new nested key path?

    Answer The two-key table stores a first-key record whose cdr is itself an association list. Inserting a new second key mutates that subtable, while a new first key links an entire subtable into the outer table. Updating arithmetic from 10 to 11 changes the existing innermost record rather than creating a duplicate. These examples use #f as the missing result, so storing #f would require a richer lookup protocol.

    When does hidden state simplify the collaboration between otherwise independent procedures?

    Answer The second program separates a Monte Carlo controller from a stateful experiment object. monte-carlo knows only that calling experiment produces the next boolean result. The supplied finite list makes this run reproducible and directly models the modular benefit of assignment.

    Which substitution and reordering arguments become invalid once an expression can mutate state?

    Answer The order probe makes schedule dependence explicit with let bindings. Calling the zero message first changes state before the one message reads it. Calling one first observes the old state. No conclusion here depends on the evaluator choosing an operand order; the two sequences are written separately and compared as data.

    Which environment is used when an expression creates a procedure, and which environment is used when that procedure is later applied?

    Answer The second program makes frame mutation explicit. Lookup finds local x before global x. set-variable-value! changes the already existing local record, while define-variable! adds y to the first frame. Neither operation changes global x. This explanatory evaluator models the listed forms directly.

    How do equal parameter names remain distinct across nested and repeated procedure applications?

    Answer make-adder records the frame where x = 5 and returns a closure that keeps access to it. Calling add-five later creates a y = 7 frame linked to that captured environment. The transcript uses pedagogical frame names to make those links explicit.

    How do mutation and identity change the meaning of familiar list operations such as append and traversal?

    Answer The cycle example changes the final cdr to point back at the first pair. Ordinary recursive list traversal would never reach the empty list. count-unique-pairs therefore records every pair identity before descending and contributes zero when memq finds a pair already seen. The program reports the cycle through eq? instead of asking the printer to expand it indefinitely.

    How can local gate rules combine into a circuit whose behavior unfolds through explicit simulated time?

    Answer The half-adder is built from or, and, inverter, and another and gate. The finite schedule first settles the zero inputs, then changes a, b, and a again. The recorded times are exact simulation events derived from the explicit delays in this model.

    How can two individually serialized resources still deadlock, and what ordering rule removes that cycle?

    Answer The transfer program orders two account locks by numeric id before acquiring either pair. Callers may request transfers in opposite account directions, but both operations use the same lock order. The sequential demonstration completes both transfers, releases both cells, and records the full state transition of the selected locking protocol.

    What changes when a numerical iteration describes every successive approximation instead of hiding all but the final answer?

    Answer sqrt-stream exposes every Newton improvement for √2. Its exact rational sequence begins 1, 3/2, 17/12, 577/408, and 665857/470832. Later consumers can inspect more guesses without changing the producer. The displayed prefix records the improvement sequence for the selected starting value.

    Which dependencies become visible when state is represented as a stream instead of hidden inside an object?

    Answer The resettable object changes its private seed in place. The functional version restarts by constructing a new stream from seed 1; the old stream remains available. Equal result lists establish agreement for this finite prefix only. The object emphasizes identity and commands, while the stream emphasizes reusable histories and explicit data flow.