A REPL hands you the answer. This one hands you the rewrite.
Chalkstep runs a hand-written Scheme interpreter one reduction at a time. Paste your own recursion and watch it rewrite itself down to a value, the way SICP chalks it on the board.
Watch someone teach recursion and they don't run the code, they rewrite it.
(factorial 3) becomes (if (= 3 0) 1 (* 3 (factorial (- 3 1)))), which becomes
(* 3 (factorial 2)), which keeps going, each new line written under the last, until a bare
number falls out the bottom. That chalkboard derivation is the substitution model, the mental
tool SICP hands you for reading
recursive procedures. And it's the one thing every online Scheme REPL throws away. You type an
expression, you get an answer, and the rewrite that connects the two happens invisibly inside a
call stack you never see.
Chalkstep is that call stack, drawn. Paste a small program, definitions and one calling expression, hit Step, and the call redraws itself in place, one reduction per click, with the sub-expression that just changed circled in chalk yellow. A REPL gives you the answer. This gives you the rewrite.
The whole thing is one function
A normal interpreter is a function evaluate(expr, env) that returns a value. It recurses into
itself, the intermediate forms live and die on the machine's stack, and you only ever see what
comes back at the end. Chalkstep is built around a different primitive. Its core is
step(expr, env) that returns not a value but the next expression: the same program with
exactly one substitution-model reduction applied. Substitute the arguments into a procedure
body, that's a step. Collapse (= 3 0) to #f, that's a step. Reduce an if whose test is
now known, that's a step.
The nice part is that step isn't a second implementation bolted onto the evaluator. Call it
in a loop until the expression stops changing and you have the evaluator, since running every
reduction to the end is exactly what evaluation is. Call it once and render the result and you
have the visualizer. The interpreter and the teaching tool are the same engine looked at from
two zoom levels, which means there's no risk of the animation quietly disagreeing with how the
language actually computes. The picture can't lie, because the picture is the computation.
One reduction, not one eval
The granularity is a deliberate choice, and it's what makes the rewrite legible rather than a
blur. A single procedure call might take a dozen steps to fully unwind: substitute the
arguments, reduce the test, pick the if branch, reduce the recursive call, and so on. A
coarser tool would run the whole call and show you the before and after. Chalkstep stops after
each individual reduction, the same way a lecturer writes one line at a time instead of erasing
the board and writing the final answer.
Steps operate on the parsed tree, not the source text, and each new tree is printed back to
Scheme (printer.ts) before it hits the screen. That's why the board always shows valid,
readable code and never a half-rewritten string with a dangling paren. The reader underneath is
hand-written, a lexer and parser with no parsing-library dependency, because a black-box eval
would hide the exact reductions the tool exists to expose.
What it deliberately can't do
This is a teaching visualizer, not a production Scheme. The language subset is small and
chosen: define, lambda, if and cond, quoting, arithmetic and comparison primitives,
pairs, and recursion, plus enough to show the SICP closure pattern ((define add5 (make-adder
5)) then (add5 10) reduces step by step, closure and all). No call/cc, no macros, no
tail-call trickery. Breadth would only dilute the one thing it does well. Even the failures are
part of the lesson: an unbalanced paren, an unbound variable, or applying a non-procedure
render as an inline chalkboard message, not a console crash, so a student pasting broken code
still learns something from the break. The interpreter core carries 195 tests at over 99% line
coverage, which is the kind of number you want behind a tool whose whole claim is that its
steps are correct.
Try it
Open Chalkstep. The factorial demo is pre-filled:
click Load, then press the right arrow (or the space bar) and watch (factorial 5) rewrite
itself all the way down to 120, each reduction circled as it happens. Rewind, scrub to any
point in the derivation, then clear the board and paste your own recursive definition to see it
unfold. The full source is on GitHub.
This post is part of the build log: every app my automated factory ships gets written up here, honestly. Browse everything at apps.charliekrug.com. Comments are open below.
Loading comments…