Next: , Previous: Emacs integration, Up: User environment


2.3 Using the module system

Scheme48 is deeply integrated with an advanced module system. For complete detail of its module system, see Module system. Briefly, however:

Scheme48's usual development system, the command processor, provides a number of commands for working with the module system. For complete details, see Module commands. Chief among these commands are ,open and ,in. `,open struct ...' makes all of the bindings from each of struct ... available in the interaction environment. Many of the sections in this manual describe one or more structures with the name they are given. For example, in order to use, or open, the multi-dimensional array library in the current interaction environment, one would enter

     ,open arrays

to the command processor. `,in struct' sets the interaction environment to be the package underlying struct. For instance, if, during development, the user decides that the package of the existing structure foo should open the structure bar, he might type

     ,in foo
     ,open bar

Module descriptions, or code in the module configuration language should be loaded into the special environment for that language with the ,config command (see Module commands). E.g., if packages.scm contains a set of module descriptions that the user wishes to load, among which is the definition of a structure frobozz which he wishes to open, he will typically send the following to the command processor prompt:

     ,config ,load packages.scm
     ,open frobozz

Note: These are commands for the interactive command processor, not special directives to store in files to work with the module system. The module language is disjoint from Scheme; for complete detail on it, see Module system.

2.3.1 Configuration mutation

(This section was derived from work copyrighted (C) 1993-2005 by Richard Kelsey, Jonathan Rees, and Mike Sperber.)

During program development, it is often desirable to make changes to packages and interfaces. In static languages, it is usually necessary to re-compile and re-link a program in order for such changes to be reflected in a running system. Even in interactive Common Lisp systems, a change to a package's exports often requires reloading clients that have already mentioned names whose bindings change. In those systems, once read resolves a use of a name to a symbol, that resolution is fixed, so a change in the way that a name resolves to a symbol can be reflected only by re-reading all such references.

The Scheme48 development environment supports rapid turnaround in modular program development by allowing mutations to a program's configuration and giving a clear semantics to such mutation. The rule is that variable bindings in a running program are always resolved according to the current structure and interface bindings, even when these bindings change as a result of edits to the configuration. For example, consider the following:

     (define-interface foo-interface (export a c))
     (define-structure foo foo-interface
       (open scheme)
       (begin (define a 1)
              (define (b x) (+ a x))
              (define (c y) (* (b a) y))))
     (define-structure bar (export d)
       (open scheme foo)
       (begin (define (d w) (+ (b w) a))))

This program has a bug. The variable named b, which is free in the definition of d, has no binding in bar's package. Suppose that b was intended to be exported by foo, but was mistakenly omitted. It is not necessary to re-process bar or any of foo's other clients at this point. One need only change foo-interface and inform the development system of that change (using, say, an appropriate Emacs command), and foo's binding of b will be found when the procedure d is called and its reference to b actually evaluated.

Similarly, it is possible to replace a structure; clients of the old structure will be modified so that they see bindings from the new one. Shadowing is also supported in the same way. Suppose that a client package C opens a structure mumble that exports a name x, and mumble's implementation obtains the binding of x from some other structure frotz. C will see the binding from frotz. If one then alters mumble so that it shadows bar's binding of x with a definition of its own, procedures in C that refer to x will subsequently automatically see mumble's definition instead of the one from frotz that they saw earlier.

This semantics might appear to require a large amount of computation on every variable reference: the specified behaviour appears to require scanning the package's list of opened structures and examining their interfaces — on every variable reference evaluated, not just at compile-time. However, the development environment uses caching with cache invalidation to make variable references fast, and most of the code is invoked only when the virtual machine traps due to a reference to an undefined variable.

2.3.2 Listing interfaces

The list-interfaces structure provides a utility for examining interfaces. It is usually opened into the config package with ,config ,open list-interfaces in order to have access to the structures & interfaces easily.

— procedure: list-interface struct-or-interface –> unspecified

Lists all of the bindings exported by struct-or-interface along with their static types. For example,

          > ,config ,open list-interfaces
          > ,config (list-interface condvars)
          condvar-has-value?        (proc (:condvar) :value)
          condvar-value             (proc (:condvar) :value)
          condvar?                  (proc (:value) :boolean)
          make-condvar              (proc (&rest :value) :condvar)
          maybe-commit-and-set-condvar! (proc (:condvar :value) :boolean)
          maybe-commit-and-wait-for-condvar (proc (:condvar) :boolean)
          set-condvar-has-value?!   (proc (:condvar :value) :unspecific)
          set-condvar-value!        (proc (:condvar :value) :unspecific)