Scheme-like procedures
Monday, December 31st, 2007Scheme procedures, like closures, carry a defining environment with them, preventing unexpected bindings. Unlike the funarg closure, it always carries the function definition. This provides static scoping of atoms for a given function definition.
Scheme does not define a data structure for a procedure, so I used the following structure: (proc <env> <args> <body>). And then I added the ability to directly apply a procedure…
(
(eq? (car fn) (quote proc))
(eval
(car(cdr(cdr(cdr fn))))
(mkenv (car(cdr(cdr fn))) args (car(cdr fn)))
)
)
And then, to satisfy the static scoping requirement of Scheme, I use the Scheme definition of a lambda expression as an eval expression that evaluates to a procedure.
(
(eq? (car form) (quote lambda))
(cons
(quote proc)
(cons
env
(cdr form)
)
)
)
One unfortunate result of the procedure data structure is the creation of a circular list. Performing a def followed by set! to bind the procedure to the variable puts the procedure in the procedure’s embedded environment a-list. The base system’s display will recurse infinitely trying to display the procedure definition.
Not having a malleable display function, I hacked up an atom containing the environment as a component of its print name. As the built-in display assumes an atom’s print name contains only characters, it will display garbage characters.
In eval, we atomize the environment…
(
(eq? (car form) (quote lambda))
(cons
(quote proc)
(cons
(cons (car(quote car)) env)
(cdr form)
)
)
)
In apply, we peel off the atom mark before giving it to mkenv…
(
(eq? (car fn) (quote proc))
(eval
(car(cdr(cdr(cdr fn))))
(mkenv (car(cdr(cdr fn))) args (cdr (car(cdr fn))))
)
)
Since set! does not create a new binding, the old quoting def (in next-m-state)…
(
(eq? (car i-state) (quote def))
(make-m-state
(cdr (get-i-list m-state))
(quote ())
(add-to-env
(make-binding
(car(cdr i-state))
(car(cdr(cdr i-state)))
)
(get-m-env m-state)
)
)
)
is replaced by an evaluating define…
(
(eq? (car i-state) (quote define))
(make-m-state
(cdr (get-i-list m-state))
(quote ())
(add-to-env
(make-binding
(car(cdr i-state))
(eval (car(cdr(cdr i-state))) (get-m-env m-state))
)
(get-m-env m-state)
)
)
)
Evaluation of a lambda expression freezes the lexical environment of that procedure. Subsequent procedure definitions using define will not be added to this captured environment.
Recursion is created by using define with a temporary value, followed by a set! expression containing the recursive lambda expression. For mutually recursive procedures at the top level, it is easiest to define all procedure names before using set! expressions. That’s easy in a script, but inconvenient in an interactive environment.