Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Purple Session

narju with no flags does not start the floor REPL. It boots the Purple session: an interactive environment that is itself a program — lib/purple.naj, one closed floor expression that constructs everything in Parts I–III at startup and ends in a read-eval-print loop. The purple blocks throughout this book are transcripts of that loop. This chapter is about the session as a user sees it; the next chapter opens the machinery.

Session state

A floor program is one closed expression; there is no floor-level define. The session has one:

(define x 42)
x
;=> 42
(set! x 43)
;=> 'x
x
;=> 43

define binds a name for the rest of the session and prints nothing on success. set! mutates an existing binding and returns the name. Neither is a floor form — the session’s environment is a frame of mutable cells, and define splices a new cell into it. The primitives are bound without cells and cannot be reassigned:

(set! + 9)
;=> ('set-immutable . '+)

Two syntaxes

The session evaluator accepts the floor forms of Part I unchanged — self-named unary lambda, one binder per let:

(let y 5 (+ y 1))
;=> 6
((lambda f n (if (eq? n 0) 1 (* n (f (- n 1))))) 5)
;=> 120

and alongside them the multi-parameter forms of the reference tower (lms-black), plus begin:

((lambda (a b) (+ a b)) 1 2)
;=> 3
(let ((a 1) (b 2)) (+ a b))
;=> 3
(begin (define tries 0) (set! tries (+ tries 1)) tries)
;=> 1

The evaluator tells them apart by shape: a symbol in the parameter or binder position means the floor form, a list means the lms-black form. Both produce the same kind of closure.

Errors

The session’s base environment raises ('unbound . name) where Pink’s nil-env answers a silent 0, so a typo is an error rather than a wrong number. The loop prints each form’s result through catch, so an error is a printed payload and the session continues:

zzz
;=> ('unbound . 'zzz)
(+ 1 2)
;=> 3

The prelude

lib/prelude.naj is loaded at boot: append, map, assoc, length, reverse, all curried in the floor style (the Prelude chapter in Part V has one entry per function):

((append '(1 2)) '(3 4))
;=> (1 2 3 4)
((map (lambda (n) (* n n))) '(1 2 3))
;=> (1 4 9)
(reverse '(1 2 3))
;=> (3 2 1)

load

(load lib/matcher.naj) evaluates a file’s forms into the session. The path is a bare symbol, not quoted — the loop treats load and define as commands, taking their arguments as unevaluated data and printing nothing on success. The matcher and µKanren chapters both start this way.

What boots

lib/purple.naj is a let-chain. In order, it:

  1. reads lib/pink-forms.naj as data and animates it with trans/evalms (the boot from the metacircular chapter), producing pink-eval and pink-evalc;
  2. runs the second and third Futamura projections — compiler and evalc-compiled are compiled at every boot, which is what the banner’s “running futamura projections” refers to;
  3. binds the helpers of Part III: compile, run-compiled, interpret, load-into;
  4. reads lib/tower.naj as data and animates it the same way, then builds one meta level and one global environment from it — the session;
  5. installs the Pink bindings of steps 1–3 into the session environment as cells (the full list is in Part V’s session-bindings chapter);
  6. loads the prelude and enters the loop.

The loop itself sits at the end of the file: read a datum; load and define are handled as above; anything else is evaluated through the tower and printed through catch. Programs, interpreters, compilers, and the session that serves them are all values built by one floor expression — the tower that expression builds is next.