diff --git a/devel/ChangeLog b/devel/ChangeLog index 4760a3aa8..8fd38f050 100644 --- a/devel/ChangeLog +++ b/devel/ChangeLog @@ -1,3 +1,8 @@ +2001-05-07 Martin Grabmueller + + * modules/module-snippets.texi: New file, documenting the module + system. Placed in `devel' for review purposes. + 2001-03-16 Martin Grabmueller * modules: New directory. diff --git a/devel/modules/module-snippets.texi b/devel/modules/module-snippets.texi new file mode 100644 index 000000000..c810db626 --- /dev/null +++ b/devel/modules/module-snippets.texi @@ -0,0 +1,734 @@ +\input texinfo +@c -*-texinfo-*- +@c %**start of header +@setfilename module-snippets.info +@settitle Module Snippets +@iftex +@afourpaper +@end iftex +@c %**end of header + +@set UPDATED 7May 2001 +@set EDITION 0.0.1 +@set VERSION 0.0.1 + +@dircategory Guile +@direntry +* module-snippets: (module-snippets). Documentation for the Guile Module System +@end direntry + + +@c --- title page starts here --- + +@titlepage +@title Module Snippets +@subtitle Documentation for the Guile Module System +@subtitle Version @value{VERSION} +@author Martin Grabmueller + +@c The following two commands +@c start the copyright page. +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 2001 Martin Grabmueller + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. +@end titlepage + +@c --- title page ends here --- + +@syncodeindex vr cp +@syncodeindex fn cp + +@c =================================================================== + +@node Top, Introduction, (dir), (dir) + +@ifinfo +This file tries to shed some light on Guile's current module system. +@end ifinfo + +@menu +* Introduction:: What is this all about? +* Module Data Type:: Modules from a data-centric view. +* Modules and Evaluation:: Interaction between the module system + and the evaluator. +* Index:: Procedure index. +@end menu + + +@c =================================================================== + +@node Introduction, Module Data Type, Top, Top +@chapter Introduction + +This document contains all information about the module system I have +been able to deduce from the source code or from mailing list +conversation. I have written down everything while trying to figure out +how the Guile module system actually works, and some of the text is +taken from commentary in the file @file{boot-9.scm}. + +The information contained herein is surely not complete, and I will be +happy to receive additions, corrections and suggestions for improving +it. + +Also note that the information contained in this document reflects the +current state as of the time writing, and the facts stated are not +guaranteed to be stable. A complete redesign of the module system is +planned for a long time now, and actually might happen some day. + +There are basically three views on the module system: + +@itemize @bullet +@item +Data-centric: The module as a data type, with all procedures for +creating and manipulating modules. This is documented in the +@ref{Module Data Type}. + +@item +Declarational: How to use declarations like @code{use-modules} or +@code{define-module} for dealing with modules and the scoping mechanisms +they provide. This issue is documented in the Guile Reference Manual, +and will not be covered in detail here. + +@item +Internal: How do the module system and the Guile evaluator interact? +This is documented in @ref{Modules and Evaluation}. +@end itemize + + +@c =================================================================== + +@node Module Data Type, Modules and Evaluation, Introduction, Top +@chapter Module Data Type + +This chapter will describe the module system from the point of the data +type @code{module}. Thus we will first see what this data type looks +like and what operations are defined on it. + +Modules in Guile are instances of a data type @code{module}. A module +has the following fields. + +@table @var +@item obarray +This is a hash table which contains all bindings made in the module. + +@item uses-list +List of the modules imported by the module. A lot of search procedures +search through all modules in this list when a specified binding cannot +be found in the module's obarray. + +@item lazy-binding-proc +A procedure for determining a binding for a module lazily. This is +invoked if a binding for a name is requested from the module, but does +not exist. The procedure can create a new binding, by loading +additional code or fetching it from another module/data structure. + +@item eval-closure +A procedure for determining a binding in a module. The evaluator uses +this procedure for accessing top-level variables. + +@item transformer +The syntax transformer used for all evaluations in the module. + +@item name +The name of the module. This is a list of symbols, such as +@code{(guile)} or @code{(ice-9 popen)}. + +@item kind +A symbol describing the type of the module. Normal modules are of type +@code{module}, interfaces are of type @code{interface} and directories +(modules which implement the hierarchical namespace) are of kind +@code{directory}). Autoload modules (placeholder for not-yet-loaded +modules) are of type @code{autoload}. FIXME: Are there other types? + +@item observers +A list of procedures to call when one of the module's bindings is +removed or modified or a new binding is created. + +@item weak-observers +Similar to @var{observers}, but this is a hash table from which the +observer procedures will magically disappear when there are no other +references to the procedure left except from the module they observe. + +@item observer-id +This is an integer, which specifies the identifer the next weak observer +will get. It is incremented each time a weak observer is added. +@end table + +@menu +* The Data Type:: The data type @code{module}. +* Module Loading:: How to load modules. +* Modules and Variables:: How do variables and modules relate. +* Iterating over Module Bindings:: How to access all bindings of a module. +* The Lazy Binder:: The lazy binding procedures. +* Module Observers:: The observer protocol. +* The Current Module:: Notion of ``current module'' in Guile. +* High-Level Module Access:: High-level access to module features. +* Recursive Namespaces:: Hierarchical organisation of namespaces. +* Module Hierarchy:: The currently implemented hierarchy. +* Modules and Interfaces:: How modules implement different scopes. +* Modules and Environments:: Relation between modules and environments. +* Modules Miscallenea:: Miscellaneous module procedures. +@end menu + + +@c =================================================================== + +@node The Data Type, Module Loading, Module Data Type, Module Data Type +@section The Data Type + +A new module is created using @code{make-module}. Several procedures +are defined for accessing the members of a module. + +@deffn procedure make-module size uses-list lazy-binding-proc +Create a new module and initialize its fields with the parameters. + +@table @var +@item size +Size of the module's obarray. + +@item uses-list +List of the modules imported by the module. + +@item lazy-binding-proc +The procedure for determining a binding for a module lazily. +@end table +@end deffn + +The various fields of a module can be accessed and modified using the +following procedures: + +@deffn procedure module-obarray module +@deffnx procedure set-module-obarray! module obj +@deffnx procedure module-uses module +@deffnx procedure set-module-uses! module obj +@deffnx procedure module-binder module +@deffnx procedure set-module-binder! module obj +@deffnx procedure module-eval-closure module +@deffnx procedure set-module-eval-closure! module obj +@deffnx procedure module-transformer module +@deffnx procedure set-module-transformer! module obj +@deffnx procedure module-name module +@deffnx procedure set-module-name! module obj +@deffnx procedure module-kind module +@deffnx procedure set-module-kind! module obj +@deffnx procedure module-observers module +@deffnx procedure set-module-observers! module obj +@deffnx procedure module-weak-observers module +@deffnx procedure module-observer-id module +@deffnx procedure set-module-observer-id! module obj +Read the corresponding member of @var{module}, or write the value +@var{obj} into the slot. +@end deffn + +@deffn procedure module? obj +This is the type predicate for modules, which will return @code{#t} if +its argument is a module, and @code{#f} otherwise. +@end deffn + +@deffn procedure make-scm-module +This will create a module which represents Guile's builtin bindings. +Initially, it is empty, but when variable lookups are made in it, the +requested bindings will get copied into the module from the internal +obarray. Bindings will get copied even if only accessed. +@end deffn + +@deffn procedure make-root-module +Create a new module which works on the internal obarrary. The +difference to the modules returned by @code{make-scm-module} is that +bindings are only copied to the obarray if they are explicitly defined. +@end deffn + + +@c =================================================================== + +@node Module Loading, Modules and Variables, The Data Type, Module Data Type +@section Module Loading + +The normal way of loading modules in Scheme programs is to use the +special form @code{use-modules}, which loads the source of one or more +specified modules and imports their public bindings into the current +module. This procedure is documented in the Guile Reference Manual, so +I will not go into details here. This section rather contains the +underlying mechanisms, on which @code{use-modules} is built. + +Normally, you will not need to create new modules explicitly, adding +definitions to it manually. For more often, you will want to load some +Scheme code from a file, and install its definitions into a module, only +exporting the public bits. This is what the procedure +@code{resolve-module} is for. + +@deffn procedure resolve-module name [maybe-autoload] +If the module @var{name} already exists, return it. Otherwise, try to +load the Scheme code for @var{name} into a newly created module, adding +the exports to the public interface. + +The parameter @var{maybe-autoload} controls the behaviour when the +module code is going to be loaded. + +@table @asis +@item @var{maybe-autoload} == @code{#f} +Do not defer loading. + +@item @var{maybe-autoload} == @code{#t} +Defer loading of the source code until a binding from the module is +requested. + +@item @var{maybe-autoload} not given +Like @var{maybe-autoload} == @code{#t}. +@end table +@end deffn + + +@c =================================================================== + +@node Modules and Variables, Iterating over Module Bindings, Module Loading, Module Data Type +@section Modules and Variables + +Modules can be regarded as mappings from symbols (variable names) to +bindings (variable locations). The procedures documented in this +section can be used to test whether such a mapping exists for a given +variable name, how to add mappings and how to retrieve a variables +value. + +We sometimes want to look for properties of a symbol just within the +obarray of one module. If the property holds, then it is said to hold +``locally'' as in, ``The symbol @code{display} is locally rebound in the +module @code{safe-guile}.'' + +Other times, we want to test for a symbol property in the obarray of +@var{m} and, if it is not found there, try each of the modules in the +uses list of @var{m}. This is the normal way of testing for some +property, so we state these properties without qualification as in: +``The symbol 'fnord is interned in module M because it is interned +locally in module @var{m2} which is a member of the uses list of +@var{m}.'' + +@deffn procedure module-locally-bound? module sym +Test if @var{sym} is bound in @var{module} directly, e.g. it does not +suffice that @var{sym} is bound in one of the used modules of +@var{module}. @dfn{Bound} means that the symbol is interned and bound +to some well-defined value. +@end deffn + +@deffn procedure module-bound? module sym +Return true if @var{sym} is bound in @var{module} or one of the modules +in @var{module}'s uses list. The search looks in all transitively used +modules. @dfn{Bound} means that the symbol is interned and bound to +some well-defined value. +@end deffn + +@deffn procedure module-symbol-locally-interned? module sym +Test if @var{sym} is interned in @var{module} directly, e.g. it does not +suffice that @var{sym} is bound in one of the used modules of +@var{module}. Unlike @code{module-locally-bound}, the symbol is not +required to be bound to a well-defined value. +@end deffn + +@deffn procedure module-symbol-interned? module sym +Return true if @var{sym} is interned in @var{module} or one of the +modules in @var{module}'s uses list. The search looks in all +transitively used modules. Unlike @code{module-bound}, the symbol is +not required to be bound to a well-defined value. +@end deffn + +@deffn procedure module-local-variable module sym +Return a variable object for @var{SYM} in the module @var{module}, or +@code{#f} if no such symbol is defined in @var{module}. If the symbols +is not found at first, but the module has a lazy binder, then try the +binder. +@end deffn + +@deffn procedure module-variable module sym +Return a variable object for @var{sym} in the module @var{module} or one +of its used modules, or @code{#f} if no such symbol is defined in +@var{module} or its uses. +@end deffn + +@deffn procedure module-symbol-local-binding module symbol [opt-value] +Return the value of the binding called @var{symbol} in @var{module}, or +@var{opt-val} if no such binding exists. If no @var{opt-value} is given +and no binding exists, an error is thrown. +@end deffn + +@deffn procedure module-symbol-binding module symbol [opt-value] +Return the value of the binding called @var{symbol} in @var{module}, or +@var{opt-val} if no such binding exists. If no @var{opt-value} is given +and no binding exists, an error is thrown. Unlike +@code{module-symbol-local-binding}, this will search all used modules as +well as @var{module}. +@end deffn + +@deffn procedure module-make-local-var! module symbol +Create a binding for a variable called @var{symbol} in @var{module} and +return the variable object representing the new location in the module. +If @var{symbol} is already defined in @var{module}, nothing happens. +@end deffn + +@deffn procedure module-add! module symbol var +Add the variable @var{var} to @var{module} under the name @var{symbol}. +@end deffn + +@deffn procedure module-remove! module symbol +Remove the binding for @var{symbol} in @var{module}. The return value +is not specified. +@end deffn + +@deffn procedure module-clear! module +Remove all bindings from @var{module}. +@end deffn + + +@c =================================================================== + +@node Iterating over Module Bindings, The Lazy Binder, Modules and Variables, Module Data Type +@section Iterating over Module Bindings + +@deffn procedure module-for-each proc module +Apply @var{proc} to every binding in @var{module}. @var{proc} is called +with two parameters, the name and variable for each binding. +@end deffn + +@deffn procedure module-map proc module +Apply @var{proc} to every binding in @var{module} and return a list of +the results of all applications of @var{proc}. @var{proc} is called +with two parameters, the name and variable for each binding. +@end deffn + + +@c =================================================================== + +@node The Lazy Binder, Module Observers, Iterating over Module Bindings, Module Data Type +@section The Lazy Binder + +The lazy binding procedures which are connected to modules are invoked +every time a binding is searched in a module, but is not present. A +binder is called with three arguments. + +When a lazy binder returns a variable object, the search is successful +and the return value will be used. If the return value is @code{#f}, +the search is continued in the modules from the uses list. FIXME: Is +this always the case or only in the standard eval closure? + +@table @var +@item module +The module for which the binding is requested. + +@item symbol +The name of the searched symbol. + +@item define? +@code{#t} if the binding should be defined, @code{#f} otherwise. +@end table + + +@c =================================================================== + +@node Module Observers, The Current Module, The Lazy Binder, Module Data Type +@section Module Observers + +A module can have a number of @dfn{observers} attached. These are +procedures which are called whenever something withing the module +changes. This can be the creation, deletion or modification of a +binding. + +When a change occurs, the procedure @code{module-modified} (documented +below in this section) will be called which in turn will apply all +observer procedures to the modified module. + +@deffn procedure module-observe module proc +Add the observer @var{proc} to @var{module} and return a pair of +@var{module} and @var{proc}. The returned value can be used with +@code{module-unobserve}. +@end deffn + +@deffn procedure module-observe-weak module proc +Add @var{proc} as a weak observer to @var{module} and erturn a pair of +@var{module} and a unique integer, the observer ID. The returned value +can be used with @code{module-unobserve}. +@end deffn + +@deffn procedure module-unobserve token +Remove an observer from a module. The module and the observer to be +removed are taken from @var{token}, which must be returned by +@code{module-observe} or @code{module-unobserve}. +@end deffn + +@deffn procedure module-modified m +Signal a modification of module @var{m} to all associated observers. +@end deffn + + + +@c =================================================================== + +@node The Current Module, High-Level Module Access, Module Observers, Module Data Type +@section The Current Module + +For all evaluations, Guile maintains a so-called @dfn{current +module}.@footnote{A current module does not exist until Guile has been +completely booted, that means until @file{boot-9.scm} has been loaded. +But this should be no issue unless you are doing weird things withe the +module system, which might be a bad idea, but YMMV.} This is used for +all top-level definitions and variable lookups. When the current module +changes, new definitions will go to the new module. The procedures in +this section manipulate the notion of the current module. + +The current module is also used when C code calls @code{scm_make_gsubr} +for creating new primitives or @code{scm_sysintern} for interning +symbols. From C, the current module can be set by calling +@code{scm_set_current_module}, which will return the old module. This +returned module can later be used to switch back to the old module after +creating a new one and installing bindings there. + + +@deffn procedure set-current-module module +Make @var{module} the current module, into which all following +definitions will go. Return the old module in effect before the call to +@code{set-current-module}. +@end deffn + +@deffn procedure current-module +Return the module which is currently registered as the @dfn{current +module}. +@end deffn + + + +@c =================================================================== + +@node High-Level Module Access, Recursive Namespaces, The Current Module, Module Data Type +@section High-Level Module Access + +The procedure in the previous chapter are not for general use. The +current chapter will document all procedures which are meant to be used +by users who need to work with modules. + +The parameter @var{module} in the following descriptions must be a +module, @var{name} must be a symbol (which most probably will need to be +quoted). + +@deffn procedure module-ref module name [default] +Return the value of a variable called @var{name} in @var{module} or any +of its used modules. If there is no such variable, then if the optional +third argument @var{default} is present, it is returned; otherwise an +error is signaled. +@end deffn + +@deffn procedure module-set! module name value +Sets the variable called @var{name} in @var{module} (or in a module that +@var{module} uses) to @var{value}; if there is no such variable, an +error is signaled. +@end deffn + +@deffn procedure module-define! module name value +Sets the variable called @var{name} in @var{module} to @var{value}; if +there is no such variable, it is added first. +@end deffn + +@deffn procedure module-defined? module name +Return @code{#t} if @var{name} is defined in @var{module} (or in a +module that @var{module} uses). +@end deffn + +@deffn procedure module-use! module interface +Add @var{interface} to the list of interfaces used by @var{module}. For +information what an @dfn{interface} is, see @ref{Modules and +Interfaces}. +@end deffn + +@deffn procedure module-export! module names +Add all variables from @var{names} (a list of symbols) to the public +interface of @var{module} (@pxref{Modules and Interfaces}). +@end deffn + +@c =================================================================== + +@node Recursive Namespaces, Module Hierarchy, High-Level Module Access, Module Data Type +@section Recursive Namespaces + +A hierarchical namespace emerges if we consider some module to be root, +and variables bound to modules as nested namespaces. + +The modules which implement the internal nodes are of kind +@code{directory}. FIXME: Is this correct? + +The routines in this chapter manage variable names in hierarchical +namespace. Each variable name is a list of elements, looked up in +successively nested modules. + +@example +(nested-ref some-root-module '(foo bar baz)) +@result{} + +@end example + +@deffn procedure nested-ref root names +Look up the variable identified by the symbol list @var{names}, starting +in the module @var{root}. +@end deffn + +@deffn procedure nested-set! root names val +Set the variable identified by the symbol list @var{names} to @var{val}, +starting the variable lookup in module @var{root}. The return value is +not specified. +@end deffn + +@deffn procedure nested-define! root names val +Set the variable identified by the symbol list @var{names} to @var{val}, +starting the variable lookup in module @var{root}. If the variable does +not exist, create it before setting its value. The return value is not +specified. +@end deffn + +@deffn procedure nested-remove! root names +Remove the variable identified by the symbol list @var{names}, starting +the variable lookup in module @var{root}. The return value is not +specified. +@end deffn + +@deffn procedure local-ref names +@deffnx procedure local-set! names val +@deffnx procedure local-define! names val +@deffnx procedure local-remove! names +Like the @code{nested-ref}, @code{nested-set!}, @code{nested-define!} +and @code{nested-remove!} procedures above, but start the variable +lookup in the module returned by @code{current-module}. +@end deffn + + +@c =================================================================== + +@node Module Hierarchy, Modules and Interfaces, Recursive Namespaces, Module Data Type +@section Module Hierarchy + +Currently, the following entries are defined in the hierarchical +namespace. + +@table @code +@item (app) +This is the root of all named objects which are not in the top level. + +@item (app modules) +This is the directory of all modules. + +@item (app modules guile) +This is the standard root module. +@end table + +User modules which are loaded into Guile as well as the modules shipped +with the Guile distribution are installed under @code{(app modules)} as +well. + + +@c =================================================================== + +@node Modules and Interfaces, Modules and Environments, Module Hierarchy, Module Data Type +@section Modules and Interfaces + +Interfaces are modules of kind @code{interface}. They always belong to +another module and contain the bindings which are exported from that +module. Interfaces are the means by which the different scopes of a +module (private vs. public bindings) are implemented. + +Every module can define a special variable called +@code{%module-public-interface}, which is bound to the module's +interface. + +Whenever a variable is exported (with the @code{export} form or the +@code{:export} keyword in the @code{define-module} form), this variable +is added to the defining module's interface. Because importing a module +means adding other modules' interfaces to the uses list, the exported +variables become visible in the importing module. + +@deffn procedure module-public-interface m +Return the public interface of module @var{m}, or @code{#f} if @var{m} +does not have a public interface. +@end deffn + +@deffn procedure set-module-public-interface! m i +Set the public interface of the module @var{m} to @var{i}. +@end deffn + + +@c =================================================================== + +@node Modules and Environments, Modules Miscallenea, Modules and Interfaces, Module Data Type +@section Modules and Environments + +An environment belongs to a specific module, which can be determined by +calling @code{environment-module}. + +@deffn procedure environment-module env +@end deffn + + +@c =================================================================== + +@node Modules Miscallenea, , Modules and Environments, Module Data Type +@section Modules Miscallenea + +This chapter contains all miscellaneous information and procedure +documentation which I have not been able to include elsewhere. If +someone knows how to include them into other chapters, suggestions are +welcome. + +@deffn procedure set-system-module! m s +Set the @code{system-module} property of the module @var{m} to @var{s}. +@var{s} should be a module telling whehter @var{m} is a system module or +not. System modules are treated specially in some cases, for example +procedures defined in system modules are excluded from backtraces. +FIXME: Is this last sentence true? +@end deffn + + +@c =================================================================== + +@node Modules and Evaluation, Index, Module Data Type, Top +@chapter Modules and Evaluation + +Up to here, we have seen how modules are implemented as a data type, +which can be manipulated by C and Scheme code to implement module system +work like providing private and public name spaces, loading of modules +and creating new modules. + +This chapter will describe the connection between the module system and +the Guile evaluator. Top-level variables (that is, variables not +lexically bound) need to be resolved in the current module, and if not +defined there, in the used modules, and so on, until the root module has +been asked for the bindings. + +First, we have to recall how Guile normally figures out the location for +a given variable when evaluating a form. The evaluator starts by +scanning the lexical environment it maintains. It first looks in each +slot in the top-most environment frame, continuing in the next frame and +so on, until it reaches the end of the lexical environment chain. + +The @sc{car} of the last pair of the environment chain is either +@code{#f}, or it is a procedure. When it is @code{#f}, the normal +system obarray is searched for the variable, otherwise the procedure is +called for returning the requested variable. This procedure is the +current module's @dfn{eval closure}, and is responsible for searching a +variable's binding, installing it if necessary. + +When a variable is finally found, the reference to the variable in the +currently executed Scheme code is replaced by a special value (a +so-called @code{gloc}), so that this environment search is not necessary +the next time the variable is looked up. + + +@c =================================================================== + +@node Index, , Modules and Evaluation, Top +@comment node-name, next, previous, up +@unnumbered Index + +@printindex cp + +@contents + +@bye