8 Environments

An environment consists of a set of bindings, and a list of zero or more references to other environments called its parents. Changing the set of bindings of an environment, or setting the referent of the reference in a binding, is a mutation of the environment. (Changing the parent list, or a referent in the list, would be a mutation of the environment too, but there is no facility provided to do it.) The Kernel data type environment is encapsulated. Among other things, there is no facility provided for enumerating all the variables exhibited by an environment (which is not required, after all, to be a finite set), and no facility for identifying the parents of an environment. Two environments are equal? iff they are eq?.

An auxiliary data type used by combiners that perform binding is ignore. The ignore type consists of a single immutable value, having external representation #ignore. The ignore type is encapsulated.

— Applicative: environment? (environment? . objects)

The primitive type predicate for type environment. environment? returns true iff all the objects in objects are of type environment.

— Applicative: ignore? (ignore? . objects)

The primitive type predicate for type ignore. ignore? returns true iff all the objects in objects are of type ignore.

— Applicative: eval (eval expression environment)

The eval applicative evaluates expression in environment, as a tail context, returning the resulting value.

— Applicative: make-environment (make-environment . environments)

The applicative constructs and returns a new environment, with initially no local bindings, and parent environments the environments listed in environments. The constructed environment internally stores its list of parents independent of the first-class list environments, so that subsequent mutation of environments will not change the parentage of the constructed environment. If the provided list environments is cyclic, the constructed environment will still check each of its parents at most once, and signal an error if no binding is found locally or in any of the parents. No two objects returned by different calls to make-environment are eq? to each other.

— Operative: $define! ($define! <definiend> <expression>)

<definiend> should be a formal parameter tree, as described below; otherwise, an error is signaled.

The $define! operative evaluates <expression> in the dynamic environment and matches <definiend> to the result in the dynamic environment, binding each symbol in definiend in the dynamic environment to the corresponding part of the result; the matching process will be further described below. The ancestors of the dynamic environment, if any, are unaffected by the matching process, as are all bindings, local to the dynamic environment, of symbols not in <definiend>. The result returned by $define! is inert.

A formal parameter tree has the following context-free structure:

          ptree:: symbol | #ignore | () | (ptree . ptree)

That is, a formal parameter tree is either a symbol, or ignore, or nil, or a pair whose car and cdr referents are formal parameter trees. A formal parameter tree must also be acyclic, and no one symbol can occur more than once in it. It is not an error for a pair in the tree to be reachable from the root by more than one path, as long as there is no cycle; but if any particular symbol were reachable from the root by more than one path, that would count as occurring more than once. Thus, if a pair is reachable by more than one path, there must be no symbols reachable from it.

Matching of a formal parameter tree t to an object o in an environment e proceeds recursively as follows. If the matching process fails, an error is signaled.

  • If t is a symbol, then t is bound to o in e.
  • If t is #ignore, no action is taken.
  • If t is nil, then o must be nil (else matching fails).
  • If t is a pair, then o must be a pair (else matching fails). The car of t is matched to the car of o in e, and the cdr of t is matched to the cdr of o in e.

 

— Operative: $let ($let <bindings> . <objects>)

<bindings> should be a finite list of formal-parameter-tree/expression pairings, each of the form (formals expression), where each formals is a formal parameter, and no symbol occurs in more than one of the formals.

The following equivalence holds:

          ($let ((form1 exp1) ... (formn expn)) . objects) ==
            (($lambda (form1 ... formn) . objects) exp1 ... expn)

Thus, the expk are first evaluated in the dynamic environment, in any order; then a child environment e of the dynamic environment is created, with the formk matched in e to the results of the evaluations of the expk; and finally the subexpressions of objects are evaluated in e from left to right, with the last (if any) evaluated as a tail context, or if objects is empty the result is inert.

— Operative: $binds? ($binds? <exp> . <symbols>)

Operative $binds evaluates <exp> in the dynamic environment; call the result envenv must be an environment. The operative is a predicate that returns true iff all its later operands, <symbols>, are visibly bound in env.

— Applicative: get-current-environment (get-current-environment)

The get-current-environment applicative returns the dynamic environment in which it is called.

— Applicative: make-kernel-standard-environment (make-kernel-standard-environment)

The make-kernel-standard-environment applicative returns a standard environment; that is, a child of the ground environment with no local bindings.

— Operative: $let* ($let* <bindings> . <body>)

<bindings> should be a finite list of formal-parameter-tree/expression pairings, each of the form (formals expression), where each formals is a formal parameter tree; <body> should be a list of expressions.

The following equivalences hold:

          ($let* () . body) == ($let () . body)
          
          ($let* ((form exp) . bindings) . body) ==
            ($let ((form exp)) ($let* bindings . body))
— Operative: $letrec ($letrec <bindings> . <body>)

<bindings> and <body> should be as described for $let.

The following equivalence holds:

          ($letrec ((form1 exp1) ... (formn expn)) . body) ==
            ($let () ($define! (form1 ... formn) (list exp1 ... expn)) . body)
— Operative: $letrec* ($letrec* <bindings> . <body>)

<bindings> and <body> should be as described for $let*.

The following equivalences hold:

          ($letrec* () . body) == ($letrec () . body)
          
          ($letrec* ((form exp) . bindings) . body) ==
            ($letrec ((form exp)) ($letrec* bindings . body))
— Operative: $let-redirect ($let-redirect <exp> <bindings> . <body>)

<bindings> and <body> should be as described for $let.

The following equivalence holds:

          ($let-redirect exp ((form1 exp1) ... (formn . body) expn)) ==
            ((eval (list $lambda (form1 ... formn) body) exp) expn ... expn)
— Operative: $let-safe ($let-safe <bindings> . <body>)

<bindings> and <body> should be as described for $let.

The following equivalence holds:

          ($let-safe bindings . body) ==
            ($let-redirect (make-kernel-standard-environment) bindings . body)
— Operative: $remote-eval ($remote-eval <exp1> <exp2>)

Operative $remote-eval evaluates <exp2> in the dynamic environment, then evaluates <exp1> as a tail context in the environment that must result from the first evaluation.

— Operative: $bindings->environment ($bindings->environment . <bindings>)

<bindings> should be as described for $let.

The following equivalence holds:

          ($bindings->environment . bindings) ==
            ($let-redirect (make-environment) bindings (get-current-environment))
— Applicative: eval-string (eval-string string environment)

string should be the external representation of a single object. If none or more than one external representation is found in string then an error is signaled.

Applicative eval-string reads an external representation from string, and evaluates the resulting object in environment, as a tail context, returning the resulting value.

— Operative: $set! ($set! <exp1> <formals> <exp2>)

<formals> should be as described for the $define! operative. The $set! operative evaluates <exp1> and <exp2> in the dynamic environment; call the results env and obj. If env is not an environment, an error is signaled. Then the operative matches <formals> to obj in environment env. Thus, the symbols of <formals> are bound in env to the corresponding parts of obj. The result returned by $set! is inert.

— Operative: $provide! ($provide! <symbols> . <body>)

<symbols> must be a finite list of symbols, containing no duplicates. <body> must be a finite list.

The $provide! operative constructs a child e of the dynamic environment d; evaluates the elements of <body> in e, from left to right, discarding all of the results; and exports all of the bindings of symbols in <symbols> from e to d, i.e., binds each symbol in d to the result of looking it up in e. The result returned by $provide! is inert.

The following equivalence holds:

          ($provide!  symbols . body) ==
          ($define!  symbols ($let () ($sequence . body) (list . symbols)))
— Operative: $import! ($import! <exp> . <symbols>)

<symbols> must be a list of symbols.

The $import! operative evaluates <exp> in the dynamic environment; call the result envenv must be an environment. Each distinct symbol s in <symbols> is evaluated in env, and s is bound in the dynamic environment to the result of this evaluation.

The following equivalence holds:

          ($import! exp . symbols) ==
          ($define! symbols ($remote-eval (list symbols) exp))