sicp.io
4.1.9 · Scanning internal definitions

An evaluator can make local initialization explicit.

A procedure-body transformation allocates every internal name first, then installs each value so mutually recursive helpers share one local environment.

Guiding question

Why must an evaluator create all internal bindings before it installs any of the procedure values?

  • Recognize the leading internal definitions in a procedure body
  • Convert procedure-definition syntax into an explicit lambda value
  • Allocate every local name with an unassigned marker before assignment
  • Preserve the original non-definition body after the assignments
  • Compare original and transformed programs on the same finite inputs
  • Inspect agreement for the displayed transformation and inputs

The scan-out pass reads a procedure body as data. It takes the leading define forms, converts procedure-definition shorthand into lambda expressions, creates one let binding for every name with an explicit unassigned marker, and places a set! for every definition before the original remaining body. The names therefore exist together before any closure is applied, which gives mutually recursive helpers one shared local environment.

The second program executes both forms. classify-original uses internal definitions directly. classify-scanned writes the transformed let and set! structure explicitly. Both return the same parity observations for 7 and 8, and equal? reports true. The finite comparison checks this transformation and makes its explicit intermediate structure visible.

SICP code1,830 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 transformation program returns one let expression with even-step and odd-step bound to quoted *unassigned* markers, followed by two set! forms and the original list body. The comparison program returns (#t ((#f #t) (#t #f)) ((#f #t) (#t #f))).

    Trace focus

    In the transformation run, separate reading quoted source data from collecting leading definitions, constructing bindings, converting procedure definitions to lambda values, constructing assignments, and appending the remaining body. In the comparison run, find the one let environment, both set! operations, and the alternating closure calls. Agreement is reported only for the selected inputs.

    Try it yourself

    Change the program and compare the result.

    Add a third internal helper that recognizes values divisible by three through three mutually recursive remainder procedures. Write both the original and explicitly scanned versions, then compare their results for 8 and 9.

    Show hint

    Allocate all three names before the assignments. Each closure calls the next remainder procedure after subtracting one, and only the remainder-zero procedure returns true at zero.

    Complete this lesson

    0 of 23 lessons complete in this chapter0%