3. Program structure

Since its origin, STklos offers a module system which can be used to organize a program into separate environments (or name spaces). The module system is directly inherited from STk. This module system is largely inspired from the one of Tung and Dybvig exposed in Tung and Dybvig paper [TuD96]. As this module system, STk and STklos modules are defined to be easily used in an interactive environment.

STklos modules provide a clean way to organize and enforce the barriers between the components of a program. At this time, the current version of Scheme was R5RS which didn’t define a module system. However, since then, R7RS has defined the notion of libraries, which are similar, in the intent, to STklos modules. Consequently, STklos offers both systems: modules and libraries (the latter being implemented on top of the former).

3.1. Modules

STklos syntax

(define-module <name> <expr1> <expr2> …​)

Define-module evaluates the expressions <expr1>, <expr2> …​ which constitute the body of the module <name> in the environment of that module. Name must be a valid symbol or a list constitued of symbols or positive integers. If name has not already been used to define a module, a new module, named name, is created. Otherwise, the expressions <expr1>, <expr2> …​ are evaluated in the environment of the (old) module <name>[1]

Definitions done in a module are local to the module and do not interact with the definitions in other modules. Consider the following definitions,

(define-module M1
   (define a 1))

(define-module M2
  (define a 2)
  (define b (* 2 x)))

(define-module (M2 m)
  (define a 100)
  (define x 200))

Here, three modules are defined and they all bind the symbol a to a value. However, since a has been defined in distinct modules they denote three different locations.

The STklos module, which is predefined, is a special module which contains all the global bindings of a R7RS program. A symbol defined in the STklos module, if not hidden by a local definition, is always visible from inside a module. So, in the previous exemple, the x symbol refers the x symbol defined in the STklos module, which is of course different of the one defined in the module (M2 m).

The result of define-module is void.

Internally, modules name are always represented by a symbol. If the module name is given as a list, the internal name is built by appending all the components of the list, separated by a '/' symbol. So the third module can be referenced with the name (M2 m) of M2/m.

STklos procedure

(find-module name)
(find-module name default)

STklos modules are first class objects and find-module returns the module object associated to name, if it exists. If there is no module associated to name, an error is signaled if no default is provided, otherwise find-module returns default.

STklos procedure

(module? object)

Returns #t if object is a module and #f otherwise.

(module? (find-module 'STklos))   => #t
(module? 'STklos)                 => #f
(module? 123 'no)                 => no

STklos procedure

(module-name module)

Returns the internal name (a symbol) associated to a module. As said before, module name is always represented as a symbol, even if expressed as a list.

(define-module (M a) )   ; or M/a
(define-module (M b) )   ; or M/b
(define-module M/c   )   ; or (M c)

(map (lambda(x) (module-name (find-module x))) '( (M a) M/b (M c) ))
                       => (M/a M/b M/c)

STklos procedure

(current-module)

Returns the current module.

(define-module M
  (display
      (cons (eq? (current-module) (find-module 'M))
            (eq? (current-module) (find-module 'STklos)))))  |- (#t . #f)

STklos syntax

(select-module <name>)

Changes the value of the current module to the module with the given name. The expressions evaluated after select-module will take place in module name environment. Module name must have been created previously by a define-module. The result of select-module is void.

Select-module is particularly useful when debugging since it allows to place toplevel evaluation in a particular module. The following transcript shows an usage of select-module. [2]):

stklos> (define foo 1)
stklos> (define-module bar (define foo 2))
stklos> foo
1
stklos> (select-module bar)
bar> foo
2
bar> (select-module stklos)
stklos>

STklos procedure

(symbol-value symbol module)
(symbol-value symbol module default)

Returns the value bound to symbol in module. If symbol is not bound, an error is signaled if no default is provided, otherwise symbol-value returns default. Module can be an object module or a module name.

STklos procedure

(symbol-value* symbol module)
(symbol-value* symbol module default)

Returns the value bound to symbol in module. If symbol is not bound, an error is signaled if no default is provided, otherwise symbol-value returns default.

Note that this function searches the value of symbol in module and in the STklos module if module is not a R7RS library.

STklos procedure

(symbol-bound? symb)
(symbol-bound? symb module)

Returns #t is symb is bound in module and #f otherwise. If module is omitted it defaults to the current module.

STklos procedure

(module-symbols module)

Returns the list of symbols defined or imported in module. Module can be an object module or a module name.

STklos procedure

(module-symbols* module)

Returns the the list of symbols acessible in module (that is the symbols defined in module and the one defined in the STklos module if module is not a R7RS library.

STklos syntax

(export <export spec1> <export spec2> …​)

Specifies the symbols which are exported (i.e. visible outside the current module). By default, symbols defined in a module are not visible outside this module, excepted if they appear in an export clause.

An <export spec> takes one of the following forms:

  • <identifier>

  • (rename <identifier1> <identifier2>)

In the first form, <identifier> names a single binding defined within or imported into the module, where the external name for the export is the same as the name of the binding within the module.

In the second form, the binding defined within or imported into the module and named by <identifier1> in each (<identifier1> <identifier2>) pairing, using <identifier2> as the external name.

The result of export is void.

the export form in STklos modules is compatible with the export clause in R7RS libraries.

STklos syntax

(import <import set1> <import set2> …​)

An import declaration provides a way to import identifiers exported by a module. Each <import set> names a set of bindings from a module and possibly specifies local names for the imported bindings. It takes one of the following forms:

  • <module name>

  • (only <import set> <identifier> …​)

  • (except <import set> <identifier> …​)

  • (prefix <import set> <identifier>)

  • (rename <import set> (<identifier1> <identifier2>) …​)

In the first form, all of the identifiers in the named module’s export clauses are imported with the same names (or the exported names if exported with rename).

The additional import set forms modify this set as follows:

  • only produces a subset of the given <import set>} including only the listed identifiers (after any renaming). It is an error if any of the listed identifiers are not found in the original set.

  • except produces a subset of the given <import set>, excluding the listed identifiers (after any renaming). It is an error if any of the listed identifiers are not found in the original set.

  • rename modifies the given <import set>, replacing each instance of <identifier1> with <identifier2>. It is an error if any of the listed <identifiers> are not found in the original set.

  • prefix automatically renames all identifiers in the given <import set>, prefixing each with the specified <identifier>.

(define-module M1
  (export a b)
  (define a 'M1-a)
  (define b 'M1-b))

(define-module M2
  (export b c d)
  (define b 'M2-b)
  (define c 'M2-c)
  (define d 'M2-d))

(define-module M3
  (import M1 M2)
  (display (list a b c d)))  |- (M1-a M2-b M2-c M2-d)

(define-module M4
  (import M2 M1)
  (display (list a b c d)))  |- (M1-a M1-b M2-c M2-d)
(define-module M1
  (export a b c d)
  (define a 1)
  (define b 2)
  (define c 3)
  (define d 4))

(define-module M2
  (import (prefix (rename (except M1 a)
                          (b bb) (c cc))
                  M1-))
  (display (module-symbols (current-module))))
                             |- (M1-bb M1-cc M1-d)

Here, M1 module exports the symbols a, b, c and d. In the M2 module, the except rule permits to import only b, c and d. With rename, identifiers b and c of M1 are renamed bb and cc. Finally, with prefix alls identifier names are prefixed by M1-.

the import form in STklos modules is compatible with the import clause in R7RS libraries.
The module STklos, which contains the global variables is always implicitly imported from a module. Furthermore, this module is always placed at the end of the list of imported modules.

Note that importing a module will try to load a file if the module is not already defined. For instance,

(define-module M
   (import (srfi 1) (foo bar baz))
    ...)

will load the file srfi/1 and foo/bar/baz modules (or libraries) if they are not yet defined (habitual rules on the load paths and the load suffixes applies to find those files).

STklos procedure

(module-imports module)

Returns the list of modules that module imports (that is, the ones it depends on).

STklos procedure

(module-exports module)

Returns the list of symbols exported by module. Note that this function returns the list of symbols given in the module export clause and that some of these symbols can be not yet defined.

(define-module M
  (export a b (rename c M-c))
  (display (module-exports (current-module))))
                         |- (a b M-c)

STklos procedure

(module-immutable! mod)

Makes the module mod immutable, so that it will be impossible to define new symbols in it or change the value of already defined ones.

STklos procedure

(module-mutable? mod)

Returns #t if mod is an immutable module and #f otherwise. Note that the SCHEME module, which contains the original bindings of the STklos at boot time, is immutable. The parameter mod can be a module object or a module name.

(module-mutable? (find-module 'STklos)) => #t
(module-mutable? (find-module 'SCHEME)) => #f
(module-mutable? 'SCHEME)               => #f

STklos syntax

(in-module mod s)
(in-module mod s default)

This form returns the value of symbol with name s in the module with name mod. If this symbol is not bound, an error is signaled if no default is provided, otherwise in-module returns default. Note that the value of s is searched in mod and all the modules it imports.

This form is in fact a shortcut. In effect,

(in-module my-module foo)

is equivalent to

(symbol-value* 'foo (find-module 'my-module))

STklos procedure

(all-modules)

Returns the list of all the living modules (or libraries). Use module-list to obtain a list of modules without libraries.

STklos procedure

(module-list)

Returns the list of all the living modules.

3.2. Libraries

The library concept is defined in R7RS ans is supported in STklos. As said before, libraries are implemented with modules. Briefly stated, the library |(lib a b)| will be implemented with a module whose name is |lib/a/b| and the |STklos| module has been deleted from the import list.

R7RS syntax

(define-library <library name> <library declaration> …​)

The form define-library is defined in R7RS. The <library name> can be a symbol or a list (as modules).

A <library declaration> is any of

  • (export <export spec> …​)

  • (import <import set> …​)

  • (begin <command or definition> …​)

  • (include <filename1> <filename2> …​)

  • (include-ci <filename1> <filename2> …​)

  • (include-library-declarations <filename1> <filename2> …​)

  • (cond-expand <clause1> <clause2> …​)

See R7RS for more information (or the specific entries in this document) about each <library declaration>.

R7RS permits to use library declarations only in a library definition; STklos permits to use them (except include-library-declarations) anywhere at toplevel.

STklos procedure

(library? object)

Returns #t if object is a module defined as a R7RS library and #f otherwise. Note that R7RS libraries, since they are implemented using STklos modules, are also modules.

(define-module a)
(define-library (b))

(module? (find-module 'a))     => #t
(module? (find-module '(b)))   => #t
(library? (find-module 'a))    => #f
(library? (find-module '(b)))  => #t

STklos procedure

(library-name lib)

Returns the name of lib if it was defined as an R7RS library, and #f if the library is anonymous. If lib is not a library, library-name raises an error. If a name is returned, it is as a list.

(define-library (example cool-library))
(library-name (find-module 'example/cool-library))   => (example cool-library)
(library-name (find-module '(example cool-library))) => (example cool-library)
(module-name  (find-module 'example/cool-library))   => example/cool-library
(module-name  (find-module '(example cool-library))) => example/cool-library

(define-module example/a-module)
(library-name (find-module 'example/a-module))     => error

(library-name quotient)                            => error

STklos procedure

(library-list)

Returns the list of all the living libraries.

3.3. Variables and Constants

R5RS syntax

(define <variable> <expresssion>)
(define (<variable> <formals>) <body>)
(define (<variable> . <formal>) <body>)

Theses forms bind an identifier to a a value.

The first form binds the <variable> to the result of the evaluation of <expression>.

The second form of define is equivalent to

(define <variable>
        (lambda (<formals>) body))

The third define form, where <formal> is a single variable, is equivalent to

(define <variable>
        (lambda <formals> body))
The define form accepts also the definition of higher order lambda as defined in SRFI-219 (Define higher-order lambda).

STklos procedure

(define-constant <variable> <expression>)
(define-constant (<variable> <formals>) body)
(define-constant (<variable> . <formal>) body)

This form is similar to define, except the binding of <variable> which is non mutable.

(define-constant a 'hello)
(set! a 'goodbye)             => error
(define a 2)                  ; is  ok (it's a new binding)
(define-constant ((foo a) b)  ; foo is (lambda (a) (lambda (b) ...)))
    ...)

STklos procedure

(symbol-immutable! symb)
(symbol-immutable! symb mod)

Makes the symbol symb in module mod immutable. If mod is not specified, the current module is used.

(define a 1)
(symbol-mutable? 'a)     => #t
(symbol-immutable! 'a)
(symbol-mutable? 'a)     => #f
(set! a 10)              => error

STklos procedure

(symbol-mutable? symb)
(symbol-mutable? symb module)

Returns #t if symb is mutable in module and #f otherwise. If module is omitted it defaults to the current module. Note that imported symbols are always not mutable.

(define-module M
   (export x)
   (define x 1))

(symbol-mutable? 'x (find-module 'M)) => #t
(symbol-mutable? 'x)                  => error, if not defined in current module
(import M)
(symbol-mutable? 'x)                  => #f

1. Documentation about hygienic macros has been stolen in the SLIB manual
1. In fact define-module on a given name defines a new module only the first time it is invoked on this name. By this way, interactively reloading a module does not define a new entity, and the other modules which use it are not altered.
2. This transcript uses the default toplevel loop which displays the name of the current module in the evaluator prompt.