X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=guile18%2Fdoc%2Fsources%2Fenv.texi;fp=guile18%2Fdoc%2Fsources%2Fenv.texi;h=59cc450160d96e6e6ce2253964f8f12fa2ac4e44;hb=139c38d9204dd07f6b235f83bae644faedbc63fd;hp=0000000000000000000000000000000000000000;hpb=652ed35a2013489d0a14fede6307cd2595abb2c4;p=lilypond.git diff --git a/guile18/doc/sources/env.texi b/guile18/doc/sources/env.texi new file mode 100644 index 0000000000..59cc450160 --- /dev/null +++ b/guile18/doc/sources/env.texi @@ -0,0 +1,1165 @@ +\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.1.10.1 2006-02-12 13:42:50 mvo 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 argumements, +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 transparenty 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 mismash 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