\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename env.info @settitle Top-level Environments in Guile @c %**end of header @setchapternewpage odd @c Changes since Jost's implementation: @c "finite environments" -> "leaf environments" @c "scm_foo_internal" -> "scm_c_foo" @c To do: @c add spec for soft environments @c When merged into the main manual, add cross-references for: @c weak references @c smobs (esp. module's mark and free functions) [[add refs for all conditions signalled]] @ifinfo Copyright 1999, 2006 Free Software Foundation, Inc. @end ifinfo @titlepage @sp 10 @comment The title is printed in a large font. @center @titlefont{Top-level Environments in Guile} @c The following two commands start the copyright page. @page @vskip 0pt plus 1filll Copyright @copyright{} 1999, 2006 Free Software Foundation, Inc. @end titlepage @node Top, Motivation, (dir), (dir) @menu * Motivation:: * Top-Level Environments in Guile:: * Modules:: @end menu @node Motivation, Top-Level Environments in Guile, Top, Top @chapter Motivation @example $Id: env.texi,v 1.2 2006-04-16 23:05:07 kryde Exp $ @end example This is a draft proposal for a new datatype for representing top-level environments in Guile. Upon completion, this proposal will be posted to the mailing list @samp{guile@@cygnus.com} for discussion, revised in light of whatever insights that may produce, and eventually implemented. Note that this is @emph{not} a proposal for a module system; rather, it is a proposal for a data structure which encapsulates the ideas one needs when writing a module system, and, most importantly, a fixed interface which insulates the interpreter from the details of the module system. Using these environments, one could implement any module system one pleased, without changing the interpreter. I hope this text will eventually become a chapter of the Guile manual; thus, the description of environments in written in the present tense, as if it were already implemented, not in the future tense. However, this text does not actually describe the present state of Guile. I'm especially interested in improving the vague, rambling presentation of environments in the section "Modules and Environments". I'm trying to orient the user for the discussion that follows, but I wonder if I'm just confusing the issue. I would appreciate suggestions if they are concrete --- please provide new wording. Note also: I'm trying out a convention I'm considering for use in the manual. When a Scheme procedure which is directly implemented by a C procedure, and both are useful to call from their respective languages, we document the Scheme procedure only, and call it a "Primitive". If a Scheme function is marked as a primitive, you can derive the name of the corresponding C function by changing @code{-} to @code{_}, @code{!} to @code{_x}, @code{?} to @code{_p}, and prepending @code{scm_}. The C function's arguments will be all of the Scheme procedure's arguments, both required and optional; if the Scheme procedure takes a ``rest'' argument, that will be a final argument to the C function. The C function's arguments, as well as its return type, will be @code{SCM}. Thus, a procedure documented like this: @deffn Primitive set-car! pair value @end deffn has a corresponding C function which would be documented like this: @deftypefn {Libguile function} SCM scm_set_car_x (SCM @var{pair}, SCM @var{value}) @end deftypefn The hope is that this will be an uncluttered way to document both the C and Scheme interfaces, without unduly confusing users interested only in the Scheme level. When there is a C function which provides the same functionality as a primitive, but with a different interface tailored for C's needs, it usually has the same name as the primitive's C function, but with the prefix @code{scm_c_} instead of simply @code{scm_}. Thus, @code{scm_c_environment_ref} is almost identical to @code{scm_environment_ref}, except that it indicates an unbound variable in a manner friendlier to C code. @node Top-Level Environments in Guile, Modules, Motivation, Top @chapter Top-Level Environments in Guile In Guile, an environment is a mapping from symbols onto variables, and a variable is a location containing a value. Guile uses the datatype described here to represent its top-level environments. @menu * Modules and Environments:: Modules are environments, with bookkeeping. * Common Environment Operations:: Looking up bindings, creating bindings, etc. * Standard Environment Types:: Guile has some fundamental environment types. * Implementing Environments:: You can extend Guile with new kinds of environments. * Switching to Environments:: Changes needed to today's Guile to implement the features described here. @end menu @node Modules and Environments, Common Environment Operations, Top-Level Environments in Guile, Top-Level Environments in Guile @section Modules and Environments Guile distinguishes between environments and modules. A module is a unit of code sharing; it has a name, like @code{(math random)}, an implementation (e.g., Scheme source code, a dynamically linked library, or a set of primitives built into Guile), and finally, an environment containing the definitions which the module exports for its users. An environment, by contrast, is simply an abstract data type representing a mapping from symbols onto variables which the Guile interpreter uses to look up top-level definitions. The @code{eval} procedure interprets its first argument, an expression, in the context of its second argument, an environment. Guile uses environments to implement its module system. A module created by loading Scheme code might be built from several environments. In addition to the environment of exported definitions, such a module might have an internal top-level environment, containing both exported and private definitions, and perhaps environments for imported definitions alone and local definitions alone. The interface described here includes a full set of functions for mutating environments, and the system goes to some length to maintain its consistency as environments' bindings change. This is necessary because Guile is an interactive system. The user may create new definitions or modify and reload modules while Guile is running; the system should handle these changes in a consistent and predictable way. A typical Guile system will have several distinct top-level environments. (This is why we call them ``top-level'', and not ``global''.) For example, consider the following fragment of an interactive Guile session: @example guile> (use-modules (ice-9 regex)) guile> (define pattern "^(..+)\\1+$") guile> (string-match pattern "xxxx") #("xxxx" (0 . 4) (0 . 2)) guile> (string-match pattern "xxxxx") #f guile> @end example @noindent Guile evaluates the expressions the user types in a top-level environment reserved for that purpose; the definition of @code{pattern} goes there. That environment is distinct from the one holding the private definitions of the @code{(ice-9 regex)} module. At the Guile prompt, the user does not see the module's private definitions, and the module is unaffected by definitions the user makes at the prompt. The @code{use-modules} form copies the module's public bindings into the user's environment. All Scheme evaluation takes place with respect to some top-level environment. Just as the procedure created by a @code{lambda} form closes over any local scopes surrounding that form, it also closes over the surrounding top-level environment. Thus, since the @code{string-match} procedure is defined in the @code{(ice-9 regex)} module, it closes over that module's top-level environment. Thus, when the user calls @code{string-match} from the Guile prompt, any free variables in @code{string-match}'s definition are resolved with respect to the module's top-level environment, not the user's. Although the Guile interaction loop maintains a ``current'' top-level environment in which it evaluates the user's input, it would be misleading to extend the concept of a ``current top-level environment'' to the system as a whole. Each procedure closes over its own top-level environment, in which that procedure will find bindings for its free variables. Thus, the top-level environment in force at any given time depends on the procedure Guile happens to be executing. The global ``current'' environment is a figment of the interaction loop's imagination. Since environments provide all the operations the Guile interpreter needs to evaluate code, they effectively insulate the interpreter from the details of the module system. Without changing the interpreter, you can implement any module system you like, as long as its efforts produce an environment object the interpreter can consult. Finally, environments may prove a convenient way for Guile to access the features of other systems. For example, one might export the The GIMP's Procedural Database to Guile as a custom environment type; this environment could create Scheme procedure objects corresponding to GIMP procedures, as the user referenced them. @node Common Environment Operations, Standard Environment Types, Modules and Environments, Top-Level Environments in Guile @section Common Environment Operations This section describes the common set of operations that all environment objects support. To create an environment object, or to perform an operation specific to a particular kind of environment, see @ref{Standard Environment Types}. In this section, the following names for formal parameters imply that the actual parameters must have a certain type: @table @var @item env an environment @item symbol a symbol @item proc a procedure @item value @itemx object an arbitrary Scheme value @end table @menu * Examining Environments:: * Changing Environments:: * Caching Environment Lookups:: * Observing Changes to Environments :: * Environment Errors:: @end menu @node Examining Environments, Changing Environments, Common Environment Operations, Common Environment Operations @subsection Examining Environments @deffn Primitive environment? object Return @code{#t} if @var{object} is an environment, or @code{#f} otherwise. @end deffn @deffn Primitive environment-ref env symbol Return the value of the location bound to @var{symbol} in @var{env}. If @var{symbol} is unbound in @var{env}, signal an @code{environment:unbound} error (@pxref{Environment Errors}). @end deffn @deffn Primitive environment-bound? env symbol Return @code{#t} if @var{symbol} is bound in @var{env}, or @code{#f} otherwise. @end deffn @deffn Primitive environment-fold env proc init Iterate over all the bindings in an environment, accumulating some value. For each binding in @var{env}, apply @var{proc} to the symbol bound, its value, and the result from the previous application of @var{proc}. Use @var{init} as @var{proc}'s third argument the first time @var{proc} is applied. If @var{env} contains no bindings, this function simply returns @var{init}. If @var{env} binds the symbol @var{sym1} to the value @var{val1}, @var{sym2} to @var{val2}, and so on, then this procedure computes: @example (@var{proc} @var{sym1} @var{val1} (@var{proc} @var{sym2} @var{val2} ... (@var{proc} @var{symn} @var{valn} @var{init}))) @end example Each binding in @var{env} is processed at most once. @code{environment-fold} makes no guarantees about the order in which the bindings are processed. If @var{env} is not modified while the iteration is taking place, @code{environment-fold} will apply @var{proc} to each binding in @var{env} exactly once. If @var{env} is modified while the iteration is taking place, we need to be more subtle in describing @code{environment-fold}'s behavior. @code{environment-fold} repeatedly applies @var{proc} to a binding which was present in @var{env} when @code{environment-fold} was invoked and is still present in @var{env}, until there are no such bindings remaining. (If no mutations take place, this definition is equivalent to the simpler one given above.) By this definition, bindings added during the iteration will not be passed to @var{proc}. Here is a function which, given an environment, constructs an association list representing that environment's bindings, using @code{environment-fold}: @example (define (environment->alist env) (environment-fold env (lambda (sym val tail) (cons (cons sym val) tail)) '())) @end example @end deffn @deftypefn {Libguile macro} int SCM_ENVP (@var{object}) Return non-zero if @var{object} is an environment. @end deftypefn @deftypefn {Libguile function} SCM scm_c_environment_ref (SCM @var{env}, SCM @var{symbol}) This C function is identical to @code{environment-ref}, except that if @var{symbol} is unbound in @var{env}, it returns the value @code{SCM_UNDEFINED}, instead of signalling an error. @end deftypefn @deftypefn {Libguile function} SCM scm_c_environment_fold (SCM @var{env}, scm_environment_folder *@var{proc}, SCM @var{data}, SCM @var{init}) This is the C-level analog of @code{environment-fold}. For each binding in @var{env}, make the call: @example (*@var{proc}) (@var{data}, @var{symbol}, @var{value}, @var{previous}) @end example @noindent where @var{previous} is the value returned from the last call to @code{*@var{proc}}, or @var{init} for the first call. If @var{env} contains no bindings, return @var{init}. @end deftypefn @deftp {Libguile data type} scm_environment_folder SCM (SCM @var{data}, SCM @var{symbol}, SCM @var{value}, SCM @var{tail}) The type of a folding function to pass to @code{scm_c_environment_fold}. @end deftp @node Changing Environments, Caching Environment Lookups, Examining Environments, Common Environment Operations @subsection Changing Environments Here are functions for changing symbols' bindings and values. Although it is common to say that an environment binds a symbol to a value, this is not quite accurate; an environment binds a symbol to a location, and the location contains a value. In the descriptions below, we will try to make clear how each function affects bindings and locations. Note that some environments may contain some immutable bindings, or may bind symbols to immutable locations. If you attempt to change an immutable binding or value, these functions will signal an @code{environment:immutable-binding} or @code{environment:immutable-location} error. However, simply because a binding cannot be changed via these functions does @emph{not} imply that it is constant. Mechanisms outside the scope of this section (say, re-loading a module's source code) may change a binding or value which is immutable via these functions. @deffn Primitive environment-define env symbol value Bind @var{symbol} to a new location containing @var{value} in @var{env}. If @var{symbol} is already bound to another location in @var{env}, that binding is replaced. The new binding and location are both mutable. The return value is unspecified. If @var{symbol} is already bound in @var{env}, and the binding is immutable, signal an @code{environment:immutable-binding} error. @end deffn @deffn Primitive environment-undefine env symbol Remove any binding for @var{symbol} from @var{env}. If @var{symbol} is unbound in @var{env}, do nothing. The return value is unspecified. If @var{symbol} is already bound in @var{env}, and the binding is immutable, signal an @code{environment:immutable-binding} error. @end deffn @deffn Primitive environment-set! env symbol value If @var{env} binds @var{symbol} to some location, change that location's value to @var{value}. The return value is unspecified. If @var{symbol} is not bound in @var{env}, signal an @code{environment:unbound} error. If @var{env} binds @var{symbol} to an immutable location, signal an @code{environment:immutable-location} error. @end deffn @node Caching Environment Lookups, Observing Changes to Environments , Changing Environments, Common Environment Operations @subsection Caching Environment Lookups Some applications refer to variables' values so frequently that the overhead of @code{environment-ref} and @code{environment-set!} is unacceptable. For example, variable reference speed is a critical factor in the performance of the Guile interpreter itself. If an application can tolerate some additional complexity, the @code{environment-cell} function described here can provide very efficient access to variable values. In the Guile interpreter, most variables are represented by pairs; the @sc{cdr} of the pair holds the variable's value. Thus, a variable reference corresponds to taking the @sc{cdr} of one of these pairs, and setting a variable corresponds to a @code{set-cdr!} operation. A pair used to represent a variable's value in this manner is called a @dfn{value cell}. Value cells represent the ``locations'' to which environments bind symbols. The @code{environment-cell} function returns the value cell bound to a symbol. For example, an interpreter might make the call @code{(environment-cell @var{env} @var{symbol} #t)} to find the value cell which @var{env} binds to @var{symbol}, and then use @code{cdr} and @code{set-cdr!} to reference and assign to that variable, instead of calling @code{environment-ref} or @var{environment-set!} for each variable reference. There are a few caveats that apply here: @itemize @bullet @item Environments are not required to represent variables' values using value cells. An environment is free to return @code{#f} in response to a request for a symbol's value cell; in this case, the caller must use @code{environment-ref} and @code{environment-set!} to manipulate the variable. @item An environment's binding for a symbol may change. For example, the user could override an imported variable with a local definition, associating a new value cell with that symbol. If an interpreter has used @code{environment-cell} to obtain the variable's value cell, it no longer needs to use @code{environment-ref} and @code{environment-set!} to access the variable, and it may not see the new binding. Thus, code which uses @code{environment-cell} should almost always use @code{environment-observe} to track changes to the symbol's binding; this is the additional complexity hinted at above. @xref{Observing Changes to Environments}. @item Some variables should be immutable. If a program uses @code{environment-cell} to obtain the value cell of such a variable, then it is impossible for the environment to prevent the program from changing the variable's value, using @code{set-cdr!}. However, this is discouraged; it is probably better to redesign the interface than to disregard such a request. To make it easy for programs to honor the immutability of a variable, @code{environment-cell} takes an argument indicating whether the caller intends to mutate the cell's value; if this argument is true, then @code{environment-cell} signals an @code{environment:immutable-location} error. Programs should therefore make separate calls to @code{environment-cell} to obtain value cells for reference and for assignment. It is incorrect for a program to call @code{environment-cell} once to obtain a value cell, and then use that cell for both reference and mutation. @end itemize @deffn Primitive environment-cell env symbol for-write Return the value cell which @var{env} binds to @var{symbol}, or @code{#f} if the binding does not live in a value cell. The argument @var{for-write} indicates whether the caller intends to modify the variable's value by mutating the value cell. If the variable is immutable, then @code{environment-cell} signals an @code{environment:immutable-location} error. If @var{symbol} is unbound in @var{env}, signal an @code{environment:unbound} error. If you use this function, you should consider using @code{environment-observe}, to be notified when @code{symbol} gets re-bound to a new value cell, or becomes undefined. @end deffn @deftypefn {Libguile function} SCM scm_c_environment_cell (SCM @var{env}, SCM @var{symbol}, int for_write) This C function is identical to @code{environment-cell}, except that if @var{symbol} is unbound in @var{env}, it returns the value @code{SCM_UNDEFINED}, instead of signalling an error. @end deftypefn [[After we have some experience using this, we may find that we want to be able to explicitly ask questions like, "Is this variable mutable?" without the annoyance of error handling. But maybe this is fine.]] @node Observing Changes to Environments , Environment Errors, Caching Environment Lookups, Common Environment Operations @subsection Observing Changes to Environments The procedures described here allow you to add and remove @dfn{observing procedures} for an environment. @menu * Registering Observing Procedures:: * Observations and Garbage Collection:: * Observing Environments from C Code:: @end menu @node Registering Observing Procedures, Observations and Garbage Collection, Observing Changes to Environments , Observing Changes to Environments @subsubsection Registering Observing Procedures A program may register an @dfn{observing procedure} for an environment, which will be called whenever a binding in a particular environment changes. For example, if the user changes a module's source code and re-loads the module, other parts of the system may want to throw away information they have cached about the bindings of the older version of the module. To support this, each environment retains a set of observing procedures which it will invoke whenever its bindings change. We say that these procedures @dfn{observe} the environment's bindings. You can register new observing procedures for an environment using @code{environment-observe}. @deffn Primitive environment-observe env proc Whenever @var{env}'s bindings change, apply @var{proc} to @var{env}. This function returns an object, @var{token}, which you can pass to @code{environment-unobserve} to remove @var{proc} from the set of procedures observing @var{env}. The type and value of @var{token} is unspecified. @end deffn @deffn Primitive environment-unobserve token Cancel the observation request which returned the value @var{token}. The return value is unspecified. If a call @code{(environment-observe @var{env} @var{proc})} returns @var{token}, then the call @code{(environment-unobserve @var{token})} will cause @var{proc} to no longer be called when @var{env}'s bindings change. @end deffn There are some limitations on observation: @itemize @bullet @item These procedures do not allow you to observe specific bindings; you can only observe an entire environment. @item These procedures observe bindings, not locations. There is no way to receive notification when a location's value changes, using these procedures. @item These procedures do not promise to call the observing procedure for each individual binding change. However, if multiple bindings do change between calls to the observing procedure, those changes will appear atomic to the entire system, not just to a few observing procedures. @item Since a single environment may have several procedures observing it, a correct design obviously may not assume that nothing else in the system has yet observed a given change. @end itemize (One weakness of this observation architecture is that observing procedures make no promises to the observer. That's fine if you're just trying to implement an accurate cache, but too weak to implement things that walk the environment tree.) @node Observations and Garbage Collection, Observing Environments from C Code, Registering Observing Procedures, Observing Changes to Environments @subsubsection Observations and Garbage Collection When writing observing procedures, pay close attention to garbage collection issues. If you use @code{environment-observe} to register observing procedures for an environment, the environment will hold a reference to those procedures; while that environment is alive, its observing procedures will live, as will any data they close over. If this is not appropriate, you can use the @code{environment-observe-weak} procedure to create a weak reference from the environment to the observing procedure. For example, suppose an interpreter uses @code{environment-cell} to reference variables efficiently, as described above in @ref{Caching Environment Lookups}. That interpreter must register observing procedures to track changes to the environment. If those procedures retain any reference to the data structure representing the program being interpreted, then that structure cannot be collected as long as the observed environment lives. This is almost certainly incorrect --- if there are no other references to the structure, it can never be invoked, so it should be collected. In this case, the interpreter should register its observing procedure using @code{environment-observe-weak}, and retain a pointer to it from the code it updates. Thus, when the code is no longer referenced elsewhere in the system, the weak link will be broken, and Guile will collect the code (and its observing procedure). @deffn Primitive environment-observe-weak env proc This function is the same as @code{environment-observe}, except that the reference @var{env} retains to @var{proc} is a weak reference. This means that, if there are no other live, non-weak references to @var{proc}, it will be garbage-collected, and dropped from @var{env}'s list of observing procedures. @end deffn @node Observing Environments from C Code, , Observations and Garbage Collection, Observing Changes to Environments @subsubsection Observing Environments from C Code It is also possible to write code that observes an environment in C. The @code{scm_c_environment_observe} function registers a C function to observe an environment. The typedef @code{scm_environment_observer} is the type a C observer function must have. @deftypefn {Libguile function} SCM scm_c_environment_observe (SCM @var{env}, scm_environment_observer *proc, SCM @var{data}, int weak_p) This is the C-level analog of the Scheme function @code{environment-observe}. Whenever @var{env}'s bindings change, call the function @var{proc}, passing it @var{env} and @var{data}. If @var{weak_p} is non-zero, @var{env} will retain only a weak reference to @var{data}, and if @var{data} is garbage collected, the entire observation will be dropped. This function returns a token, with the same meaning as those returned by @code{environment-observe}. @end deftypefn @deftp {Libguile data type} scm_environment_observer void (SCM @var{env}, SCM @var{data}) The type for observing functions written in C. A function meant to be passed to @code{scm_c_environment_observe} should have the type @code{scm_environment_observer}. @end deftp Note that, like all other primitives, @code{environment-observe} is also available from C, under the name @code{scm_environment_observe}. @node Environment Errors, , Observing Changes to Environments , Common Environment Operations @subsection Environment Errors Here are the error conditions signalled by the environment routines described above. In these conditions, @var{func} is a string naming a particular procedure. @deffn Condition environment:unbound func message args env symbol By calling @var{func}, the program attempted to retrieve the value of @var{symbol} in @var{env}, but @var{symbol} is unbound in @var{env}. @end deffn @deffn Condition environment:immutable-binding func message args env symbol By calling @var{func}, the program attempted to change the binding of @var{symbol} in @var{env}, but that binding is immutable. @end deffn @deffn Condition environment:immutable-location func message args env symbol By calling @var{func}, the program attempted to change the value of the location to which @var{symbol} is bound in @var{env}, but that location is immutable. @end deffn @node Standard Environment Types, Implementing Environments, Common Environment Operations, Top-Level Environments in Guile @section Standard Environment Types Guile supports several different kinds of environments. The operations described above are actually only the common functionality provided by all the members of a family of environment types, each designed for a separate purpose. Each environment type has a constructor procedure for building elements of that type, and extends the set of common operations with its own procedures, providing specialized functions. For an example of how these environment types work together, see @ref{Modules of Interpreted Scheme Code}. Guile allows users to define their own environment types. Given a set of procedures that implement the common environment operations, Guile will construct a new environment object based on those procedures. @menu * Leaf Environments:: A simple set of bindings. * Eval Environments:: Local definitions, shadowing imported definitions. * Import Environments:: The union of a list of environments. * Export Environments:: A selected subset of an environment. * General Environments:: Environments implemented by user functions. @end menu @node Leaf Environments, Eval Environments, Standard Environment Types, Standard Environment Types @subsection Leaf Environments A @dfn{leaf} environment is simply a mutable set of definitions. A mutable environment supports no operations beyond the common set. @deffn Primitive make-leaf-environment Create a new leaf environment, containing no bindings. All bindings and locations in the new environment are mutable. @end deffn @deffn Primitive leaf-environment? object Return @code{#t} if @var{object} is a leaf environment, or @var{#f} otherwise. @end deffn In Guile, each module of interpreted Scheme code uses a leaf environment to hold the definitions made in that module. Leaf environments are so named because their bindings are not computed from the contents of other environments. Most other environment types have no bindings of their own, but compute their binding sets based on those of their operand environments. Thus, the environments in a running Guile system form a tree, with interior nodes computing their contents from their child nodes. Leaf environments are the leaves of such trees. @node Eval Environments, Import Environments, Leaf Environments, Standard Environment Types @subsection Eval Environments A module's source code refers to definitions imported from other modules, and definitions made within itself. An @dfn{eval} environment combines two environments --- a @dfn{local} environment and an @dfn{imported} environment --- to produce a new environment in which both sorts of references can be resolved. @deffn Primitive make-eval-environment local imported Return a new environment object @var{eval} whose bindings are the union of the bindings in the environments @var{local} and @var{imported}, with bindings from @var{local} taking precedence. Definitions made in @var{eval} are placed in @var{local}. Applying @code{environment-define} or @code{environment-undefine} to @var{eval} has the same effect as applying the procedure to @var{local}. This means that applying @code{environment-undefine} to a symbol bound in @var{imported} and free in @var{local} has no effect on the bindings visible in @var{eval}, which may be surprising. Note that @var{eval} incorporates @var{local} and @var{imported} @emph{by reference} --- if, after creating @var{eval}, the program changes the bindings of @var{local} or @var{imported}, those changes will be visible in @var{eval}. Since most Scheme evaluation takes place in @var{eval} environments, they transparently cache the bindings received from @var{local} and @var{imported}. Thus, the first time the program looks up a symbol in @var{eval}, @var{eval} may make calls to @var{local} or @var{imported} to find their bindings, but subsequent references to that symbol will be as fast as references to bindings in leaf environments. In typical use, @var{local} will be a leaf environment, and @var{imported} will be an import environment, described below. @end deffn @deffn Primitive eval-environment? object Return @code{#t} if @var{object} is an eval environment, or @code{#f} otherwise. @end deffn @deffn Primitive eval-environment-local env @deffnx Primitive eval-environment-imported env Return the @var{local} or @var{imported} environment of @var{env}; @var{env} must be an eval environment. @end deffn @node Import Environments, Export Environments, Eval Environments, Standard Environment Types @subsection Import Environments An @dfn{import} environment combines the bindings of a set of argument environments, and checks for naming clashes. @deffn Primitive make-import-environment imports conflict-proc Return a new environment @var{imp} whose bindings are the union of the bindings from the environments in @var{imports}; @var{imports} must be a list of environments. That is, @var{imp} binds @var{symbol} to @var{location} when some element of @var{imports} does. If two different elements of @var{imports} have a binding for the same symbol, apply @var{conflict-proc} to the two environments. If the bindings of any of the @var{imports} ever changes, check for conflicts again. All bindings in @var{imp} are immutable. If you apply @code{environment-define} or @code{environment-undefine} to @var{imp}, Guile will signal an @code{environment:immutable-binding} error. However, notice that the set of bindings in @var{imp} may still change, if one of its imported environments changes. @end deffn @deffn Primitive import-environment? object Return @code{#t} if @var{object} is an import environment, or @code{#f} otherwise. @end deffn @deffn Primitive import-environment-imports env Return the list of @var{env}'s imported environments; @var{env} must be an import env. @end deffn @deffn Primitive import-environment-set-imports! env imports Change @var{env}'s list of imported environments to @var{imports}, and check for conflicts. @end deffn I'm not at all sure about the way @var{conflict-proc} works. I think module systems should warn you if it seems you're likely to get the wrong binding, but exactly how and when those warnings should be generated, I don't know. @node Export Environments, General Environments, Import Environments, Standard Environment Types @subsection Export Environments An export environment restricts an environment a specified set of bindings. @deffn Primitive make-export-environment private signature Return a new environment @var{exp} containing only those bindings in @var{private} whose symbols are present in @var{signature}. The @var{private} argument must be an environment. The environment @var{exp} binds @var{symbol} to @var{location} when @var{env} does, and @var{symbol} is exported by @var{signature}. @var{Signature} is a list specifying which of the bindings in @var{private} should be visible in @var{exp}. Each element of @var{signature} should be a list of the form: @example (@var{symbol} @var{attribute} ...) @end example @noindent where each @var{attribute} is one of the following: @table @asis @item the symbol @code{mutable-location} @var{exp} should treat the location bound to @var{symbol} as mutable. That is, @var{exp} will pass calls to @var{env-set!} or @code{environment-cell} directly through to @var{private}. @item the symbol @code{immutable-location} @var{exp} should treat the location bound to @var{symbol} as immutable. If the program applies @code{environment-set!} to @var{exp} and @var{symbol}, or calls @code{environment-cell} to obtain a writable value cell, @code{environment-set!} will signal an @code{environment:immutable-location} error. Note that, even if an export environment treats a location as immutable, the underlying environment may treat it as mutable, so its value may change. @end table It is an error for an element of @var{signature} to specify both @code{mutable-location} and @code{immutable-location}. If neither is specified, @code{immutable-location} is assumed. As a special case, if an element of @var{signature} is a lone symbol @var{sym}, it is equivalent to an element of the form @code{(@var{sym})}. All bindings in @var{exp} are immutable. If you apply @code{environment-define} or @code{environment-undefine} to @var{exp}, Guile will signal an @code{environment:immutable-binding} error. However, notice that the set of bindings in @var{exp} may still change, if the bindings in @var{private} change. @end deffn @deffn Primitive export-environment? object Return @code{#t} if @var{object} is an export environment, or @code{#f} otherwise. @end deffn @deffn Primitive export-environment-private env @deffnx Primitive export-environment-set-private! env @deffnx Primitive export-environment-signature env @deffnx Primitive export-environment-set-signature! env Accessors and mutators for the private environment and signature of @var{env}; @var{env} must be an export environment. @end deffn @node General Environments, , Export Environments, Standard Environment Types @subsection General Environments [[user provides the procedures]] [[A observers B and C; B observes C; C changes; A should only be notified once, right?]] [[observation loops?]] @node Implementing Environments, Switching to Environments, Standard Environment Types, Top-Level Environments in Guile @section Implementing Environments This section describes how to implement new environment types in Guile. Guile's internal representation of environments allows you to extend Guile with new kinds of environments without modifying Guile itself. Every environment object carries a pointer to a structure of pointers to functions implementing the common operations for that environment. The procedures @code{environment-ref}, @code{environment-set!}, etc. simply find this structure and invoke the appropriate function. [[It would be nice to have an example around here. How about a persistent environment, bound to a directory, where ref and set actually access files? Ref on a directory would return another environment... Hey, let's import my home directory!]] @menu * Environment Function Tables:: * Environment Data:: * Environment Example:: @end menu @node Environment Function Tables, Environment Data, Implementing Environments, Implementing Environments @subsection Environment Function Tables An environment object is a smob whose @sc{cdr} is a pointer to a pointer to a @code{struct environment_funcs}: @example struct environment_funcs @{ SCM (*ref) (SCM self, SCM symbol); SCM (*fold) (SCM self, scm_environment_folder *proc, SCM data, SCM init); void (*define) (SCM self, SCM symbol, SCM value); void (*undefine) (SCM self, SCM symbol); void (*set) (SCM self, SCM symbol, SCM value); SCM (*cell) (SCM self, SCM symbol, int for_write); SCM (*observe) (SCM self, scm_environment_observer *proc, SCM data, int weak_p); void (*unobserve) (SCM self, SCM token); SCM (*mark) (SCM self); scm_sizet (*free) (SCM self); int (*print) (SCM self, SCM port, scm_print_state *pstate); @}; @end example You can use the following macro to access an environment's function table: @deftypefn {Libguile macro} struct environment_funcs *SCM_ENVIRONMENT_FUNCS (@var{env}) Return a pointer to the @code{struct environment_func} for the environment @var{env}. If @var{env} is not an environment object, the behavior of this macro is undefined. @end deftypefn Here is what each element of @var{env_funcs} must do to correctly implement an environment. In all of these calls, @var{self} is the environment whose function is being invoked. @table @code @item SCM ref (SCM @var{self}, SCM @var{symbol}); This function must have the effect described above for the C call: @example scm_c_environment_ref (@var{self}, @var{symbol}) @end example @xref{Examining Environments}. Note that the @code{ref} element of a @code{struct environment_funcs} may be zero if a @code{cell} function is provided. @item SCM fold (SCM self, scm_environment_folder *proc, SCM data, SCM init); This function must have the effect described above for the C call: @example scm_c_environment_fold (@var{self}, @var{proc}, @var{data}, @var{init}) @end example @xref{Examining Environments}. @item void define (SCM self, SCM symbol, SCM value); This function must have the effect described above for the Scheme call: @example (environment-define @var{self} @var{symbol} @var{value}) @end example @xref{Changing Environments}. @item void undefine (SCM self, SCM symbol); This function must have the effect described above for the Scheme call: @example (environment-undefine @var{self} @var{symbol}) @end example @xref{Changing Environments}. @item void set (SCM self, SCM symbol, SCM value); This function must have the effect described above for the Scheme call: @example (environment-set! @var{self} @var{symbol} @var{value}) @end example @xref{Changing Environments}. Note that the @code{set} element of a @code{struct environment_funcs} may be zero if a @code{cell} function is provided. @item SCM cell (SCM self, SCM symbol, int for_write); This function must have the effect described above for the C call: @example scm_c_environment_cell (@var{self}, @var{symbol}) @end example @xref{Caching Environment Lookups}. @item SCM observe (SCM self, scm_environment_observer *proc, SCM data, int weak_p); This function must have the effect described above for the C call: @example scm_c_environment_observe (@var{env}, @var{proc}, @var{data}, @var{weak_p}) @end example @xref{Observing Changes to Environments}. @item void unobserve (SCM self, SCM token); Cancel the request to observe @var{self} that returned @var{token}. @xref{Observing Changes to Environments}. @item SCM mark (SCM self); Set the garbage collection mark all Scheme cells referred to by @var{self}. Assume that @var{self} itself is already marked. Return a final object to be marked recursively. @item scm_sizet free (SCM self); Free all non-cell storage associated with @var{self}; return the number of bytes freed that were obtained using @code{scm_must_malloc} or @code{scm_must_realloc}. @item SCM print (SCM self, SCM port, scm_print_state *pstate); Print an external representation of @var{self} on @var{port}, passing @var{pstate} to any recursive calls to the object printer. @end table @node Environment Data, Environment Example, Environment Function Tables, Implementing Environments @subsection Environment Data When you implement a new environment type, you will likely want to associate some data of your own design with each environment object. Since ANSI C promises that casts will safely convert between a pointer to a structure and a pointer to its first element, you can have the @sc{cdr} of an environment smob point to your structure, as long as your structure's first element is a pointer to a @code{struct environment_funcs}. Then, your code can use the macro below to retrieve a pointer to the structure, and cast it to the appropriate type. @deftypefn {Libguile macro} struct environment_funcs **SCM_ENVIRONMENT_DATA (@var{env}) Return the @sc{cdr} of @var{env}, as a pointer to a pointer to an @code{environment_funcs} structure. @end deftypefn @node Environment Example, , Environment Data, Implementing Environments @subsection Environment Example [[perhaps a simple environment based on association lists]] @node Switching to Environments, , Implementing Environments, Top-Level Environments in Guile @section Switching to Environments Here's what we'd need to do to today's Guile to install the system described above. This work would probably be done on a branch, because it involves crippling Guile while a lot of work gets done. Also, it could change the default set of bindings available pretty drastically, so the next minor release should not contain these changes. After each step here, we should have a Guile that we can at least interact with, perhaps with some limitations. @itemize @bullet @item For testing purposes, make an utterly minimal version of @file{boot-9.scm}: no module system, no R5RS, nothing. I think a simple REPL is all we need. @item Implement the environment datatypes in libguile, and test them using this utterly minimal system. @item Change the interpreter to use the @code{environment-cell} and @code{environment-observe} instead of the symbol value slots, first-class variables, etc. Modify the rest of libguile as necessary to register all the primitives in a single environment. We'll segregate them into modules later. @item Reimplement the current module system in terms of environments. It should still be in Scheme. @item Reintegrate the rest of @file{boot-9.scm}. This might be a good point to move it into modules. @item Do some profiling and optimization. @end itemize Once this is done, we can make the following simplifications to Guile: @itemize @bullet @item A good portion of symbols.c can go away. Symbols no longer need value slots. The mishmash of @code{scm_sym2ovcell}, @code{scm_intern_obarray_soft}, etc. can go away. @code{intern} becomes simpler. @item Remove first-class variables: @file{variables.c} and @file{variables.h}. @item Organize the primitives into environments. @item The family of environment types is clearly an abstract class/concrete subclass arrangement. We should provide GOOPS classes/metaclasses that make defining new environment types easy and consistent. @end itemize @node Modules, , Top-Level Environments in Guile, Top @chapter Modules The material here is just a sketch. Don't take it too seriously. The point is that environments allow us to experiment without getting tangled up with the interpreter. @menu * Modules of Guile Primitives:: * Modules of Interpreted Scheme Code:: @end menu @node Modules of Guile Primitives, Modules of Interpreted Scheme Code, Modules, Modules @section Modules of Guile Primitives @node Modules of Interpreted Scheme Code, , Modules of Guile Primitives, Modules @section Modules of Interpreted Scheme Code If a module is implemented by interpreted Scheme code, Guile represents it using several environments: @table @asis @item the @dfn{local} environment This environment holds all the definitions made locally by the module, both public and private. @item the @dfn{import} environment This environment holds all the definitions this module imports from other modules. @item the @dfn{evaluation} environment This is the environment in which the module's code is actually evaluated, and the one closed over by the module's procedures, both public and private. Its bindings are the union of the @var{local} and @var{import} environments, with local bindings taking precedence. @item the @dfn{exported} environment This environment holds the module's public definitions. This is the only environment that the module's users have access to. It is the @var{evaluation} environment, restricted to the set of exported definitions. @end table Each of these environments is implemented using a separate environment type. Some of these types, like the evaluation and import environments, actually just compute their bindings by consulting other environments; they have no bindings in their own right. They implement operations like @code{environment-ref} and @code{environment-define} by passing them through to the environments from which they are derived. For example, the evaluation environment will pass definitions through to the local environment, and search for references and assignments first in the local environment, and then in the import environment. @bye