TOC »
Modules
To allow control over visible bindings and to organize code in namespaces, a module system is available. A module defines a set of toplevel expressions that are initially evaluated in an empty syntactical environment. By importing other modules, exported value- and syntax-bindings are made visible inside the environment of the module that imports them.
Note that modules are purely syntactical - they do not change the control flow or delay the execution of the contained toplevel forms. The body of a module is executed at load-time, when code is loaded or imported, just like normal toplevel expressions. Exported syntax-definitions are compiled as well, and can be accessed in interpreted or compiled code by loading and importing the compiled file that contains the module.
Imported toplevel bindings are mutable and can be assigned (with set!), any modifications to these will change the global value and will be visible to other modules that export or import the same toplevel binding.
A module is initially empty (has no visible bindings with the exception of import and cond-expand). You must at least import the scheme module to do anything useful. To access any of the non-standard macros and procedures, import the (chicken base) module.
CHICKEN's module system has the following features:
- Separation of compile/expansion-time and run-time code is provided, which allows cross compilation
- Supports batch-compilation of separate compilation units
- Imports can be lexically scoped
- Parameterized modules are supported
At toplevel and outside of a module, the initially available bindings are everything that is exported from the scheme, (chicken base) and (chicken syntax) modules.
module
- (module NAME (EXPORT ...) BODY ...)syntax
- (module NAME (EXPORT ...) FILENAME)syntax
- (module NAME * BODY ...)syntax
- (module NAME = (FUNCTORNAME MODULENAME1 ...))syntax
- (module NAME = FUNCTORNAME BODY ...)syntax
Defines a module with the name NAME, a set of exported bindings and a contained sequence of toplevel expressions that are evaluated in an empty syntactical environment.
NAME and FUNCTORNAME should be symbols or lists of symbols and integers, where (foo bar baz) is equivalent to foo.bar.baz.
(EXPORT ...) should be an export-specification which holds a list of identifiers to be exported from the module and which should be visible when imported into another module or the toplevel environment. EXPORT may have any of the following forms:
IDENTIFIER names a value- or syntax binding to be exported.
(IDENTIFIER1 ...) or (syntax: IDENTIFIER1 ...) exports IDENTIFIER1 (which should name a macro) and also arranges for the remaining identifiers in the list to be visible as value bindings in the expansion of the macro (this is a hint to the module expander to export bindings referenced by syntax-definitions which make use of them, but which would normally be internal to the module - which gives more opportunities for optimization).
(interface: INTERFACENAME) adds all exports defined for the given interface to be added to the list of exported identifiers of this module.
As a special case, specifying * instead of an export-list will export all definitions. As another special case, the export-list may be a symbol naming an interface.
When the BODY consists of a single string, it is treated like (include FILENAME).
(module NAME = (FUNCTORNAME MODULENAME1 ...)) instantiates a functor (see below for information about functors).
(module NAME = FUNCTORNAME BODY ...) is a special form of functor instantiation where the BODY implements a module satisfying a single functor argument to FUNCTORNAME.
Nested modules, modules not at toplevel (i.e. local modules) or mutually recursive modules are not supported.
When compiled, the module information, including exported syntax is stored in the generated binary and available when loading it into interpreted or compiled code. Note that this is different to normal syntax (outside of module declarations), which are normally not exported from compiled code.
Note that the module system is only a device for controlling the mapping of identifiers to value or syntax bindings. Modules do not instantiate separate environments that contain their own bindings, as do many other module systems. Redefinition or assignment of value or syntax bindings will modify the original, imported definition.
Syntax expansions may result in module-definitions, but must be at toplevel.
export
- (export EXPORT ...)syntax
Allows augmenting module-exports from inside the module-body. EXPORT is if the same form as an export-specifier in a module export list. An export must precede its first occurrence (either use or definition).
If used outside of a module, then this form does nothing.
export/rename
- (export/rename (NAME EXPORT) ...)syntax
Allows augmenting module-exports from inside the module-body. Each argument should be a two-element list containing the name of the local value- or syntax-definition (NAME) and the name under which the definition should be exported (EXPORT).
If used outside of a module, then this form does nothing.
import
- (import IMPORT ...)syntax
Imports module bindings into the current syntactical environment. The visibility of any imported bindings is limited to the current module, if used inside a module-definition, or to the current compilation unit, if compiled and used outside of a module.
Importing a module will also load or link its associated library when needed.
IMPORT may be a module name or an import specifier, where a module name is either a symbol or a list of symbols and integers. An IMPORT defines a set of bindings that are to be made visible in the current scope.
only
[import specifier] (only IMPORT IDENTIFIER ...)
Only import the listed value- or syntax bindings from the set given by IMPORT.
except
[import specifier] (except IMPORT IDENTIFIER ...)
Remove the listed identifiers from the import-set defined by IMPORT.
rename
[import specifier] (rename IMPORT (OLD1 NEW1) ...)
Renames identifiers imported from IMPORT.
prefix
[import specifier] (prefix IMPORT SYMBOL)
Prefixes all imported identifiers with SYMBOL.
import-syntax
- (import-syntax IMPORT ...)syntax
Similar to import but only import syntactic definitions such as macros, as well as identifiers, but does not load or link the library containing the module.
import-for-syntax
- (import-for-syntax IMPORT ...)syntax
Similar to import, but imports exported bindings of a module into the environment in which macro transformers are evaluated.
Note: currently this isn't fully correct - value bindings are still imported into the normal environment because a separate import environment for syntax has not been implemented (syntactic bindings are kept separate correctly).
import-syntax-for-syntax
- (import-syntax-for-syntax IMPORT ...)syntax
Combination of import-syntax and import-for-syntax. Loads syntactic definitions and valinside the environment in which macro transformers are evaluated but do not load the associated library.
reexport
- (reexport IMPORT ...)syntax
Imports IMPORT ... and automatically exports all imported identifiers. This can be used to build compound modules: modules that just extend other modules:
(module r4rs () (import scheme (chicken module)) (reexport (except scheme dynamic-wind values call-with-values eval scheme-report-environment null-environment interaction-environment)))
define-interface
- (define-interface INTERFACENAME (EXPORT ...))syntax
Defines an interface, a group of exports that can be used in module-definitions using the (interface: INTERFACE) syntax. See the definition of module above for an explanation of EXPORT specifications.
Interface names use a distinct global namespace. Interfaces defined inside modules are not visible outside of the module body.
import libraries
import libraries allow the syntactical (compile-time) and run-time parts of a compiled module to be separated into a normal compiled file and a shared library that only contains macro definitions and module information. This reduces the size of executables and simplifies compiling code that uses modules for a different architecture than the machine the compiler is executing on (i.e. "cross" compilation).
By using the emit-import-library compiler-option or declaration, a separate file is generated that only contains syntactical information (including macros) for a module. import will automatically find and load an import library for a currently unknown module, if the import- library is either in the extension repository or the current include path. Interpreted code can simply load the import library to make the module-definition available. Syntax-support definitions defined with define-for-syntax and expansion-time expressions of the form (begin-for-syntax ...) will be added to import libraries to make them available for exported syntax. Note that these definitions will ruthlessly pollute the toplevel namespace and so they should be used sparingly.
Predefined modules
Import libraries for the following modules are initially available outside of a module:
[module] scheme [module] (chicken base) [module] (chicken syntax)
Every other module needs to be imported explicitly to have access to its exported identifiers.
Examples of using modules
Here is a silly little test module to demonstrate how modules are defined and used:
;; hello.scm (module test (hello greet) (import scheme) (define-syntax greet (syntax-rules () ((_ whom) (begin (display "Hello, ") (display whom) (display " !\n") ) ) ) ) (define (hello) (greet "world") ) )
The module test exports one value (hello) and one syntax binding (greet). To use it in csi, the interpreter, simply load and import it:
#;1> ,l hello.scm ; loading hello.scm ... ; loading /usr/local/lib/chicken/4/scheme.import.so ... #;1> (import test) #;2> (hello) Hello, world ! #;3> (greet "you") Hello, you !
The module can easily be compiled
% csc -s hello.scm
and used in an identical manner:
#;1> ,l hello.so ; loading hello.so ... #;1> (import test) #;2> (hello) Hello, world ! #;3> (greet "you") Hello, you !
If you want to keep macro-definitions in a separate file, use import libraries:
% csc -s hello.scm -j test % csc -s test.import.scm
#;1> ,l hello.so ; loading hello.so ... #;1> (import test) ; loading ./test.import.so ... #;2> (hello) Hello, world ! #;3> (greet "you") Hello, you !
If an import library (compiled or in source-form) is located somewhere in the extensions-repository or include path, it is automatically loaded on import. Otherwise you have to load it manually:
#;1> ,l hello.so ; loading hello.so ... #;1> ,l test.import.so ; loading test.import.so ... #;1> (import test) #;2>
Note that you must use import libraries if you compile code that depends on other modules. The compiler will not execute the modules that are referred to by compiled code, and thus the binding information and exported syntax of the former must be available separately.
Example of compiling modules and linking them into an executable
Here is a test module, in the file mymod.scm:
(module mymod (hello) (import scheme) (define (hello) (display "Hello, World, I'm in mymod!") (newline)))
Here is the main module, in the file trymod.scm:
(module trymod () (import scheme) (import mymod) (display "I'm in trymod!") (newline) (hello) (display "Now I'm back in trymod!") (newline))
You can compile mymod.scm into a shared object and compile trymod.scm into an executable trymod that uses that shared object like this:
csc -s -J mymod.scm csc trymod.scm
You can execute trymod and it will load the shared object mymod.so. When loading a shared object, the CHICKEN runtime uses the libld API to obtain the entry point ("C_toplevel") to invoke top-level initialization code of the module (which also setups up global bindings, etc.). However, if you move the trymod executable to another directory, it won't be able to find mymod.so to load it. If you want include the object into the executable directly, it needs to have a unique entry point name, separate from any other entry point of other linked modules.
To give the module a name, we pass the "-unit modulename" argument to csc, and the name of the module is added to the entry point, so the entry point for mymod would become "C_mymod_toplevel". Then the "-uses modulename" argument is passed to csc while compiling and linking trymod so it knows to use that entry point.
To compile mymod.scm and trymod.scm and link them into the executable trymod, issue the following commands:
csc -c -J mymod.scm -unit mymod -o mymod.o csc -o trymod mymod.o -uses mymod trymod.scm
This creates an executable that is dynamically linked against libchicken.so, but which includes the mymod.o object file directly.
To create an executable that is statically linked, issue the following commands:
csc -c -static -J mymod.scm -unit mymod -o mymod.o csc -o trymod -static mymod.o -uses mymod trymod.scm
If you later add another module you'd need to compile it similar to how mymod.scm is compiled and add a "modulename.o -uses modulename" to the csc command that compiles trymod.scm.
It is possible to use the csm program installed by the csm egg to do this automatically. To produce a dynamically linked program you would do:
csm -program trymod
To produce a statically linked program you would do:
csm -static -program trymod
Functors
A functor is a higher-order module that can be parameterized with other modules. A functor defines the body of a module for a set of argument modules and can be instantiated with concrete module names specializing the code contained in the functor. This is best explained with a silly and pointless example:
(functor (squaring-functor (M (multiply))) (square) (import scheme M) (define (square x) (multiply x x)))
This defines a generic "squaring" operation that uses multiply, a procedure (or macro!) exported by the as-yet-unknown module M. Now let's instantiate the functor for a specific input module:
(module nums (multiply) (import scheme) (define (multiply x y) (* x y))) (module number-squarer = (squaring-functor nums)) (import number-squarer) (square 3) ===> 9
We can easily instantiate the functor for other inputs:
(module stars (multiply) (import scheme) (define (list-tabulate n f) (let loop ((i 0)) (if (= i n) '() (cons (f i) (loop (+ i 1)))))) (define (multiply x y) (list-tabulate x (lambda _ (list-tabulate y (lambda _ '*)))))) (module star-squarer = (squaring-functor stars)) (import star-squarer) (square 3) ===> ((* * *) (* * *) (* * *))
So whenever you have a generic algorithm it can be packaged into a functor and specialized for specific input modules. The instantiation will check that the argument modules match the required signature, (multiply) in the case above. The argument module must export at least the signature given in the functor definition. You can use define-interface to reduce typing and give a more meaningful name to a set of exports.
The general syntax of a functor definition looks like this:
- (functor (FUNCTORNAME (ARGUMENTMODULE1 EXPORTS1) ...) FUNCTOREXPORTS BODY)syntax
Defines a "functor", a parameterized module.
This functor definition does not generate any code. This is done by instantiating the functor for specific input modules:
(module MODULENAME = (FUNCTORNAME MODULENAME1 ...))
Inside BODY, references to ARGUMENTMODULE will be replaced by the corresponding MODULENAME argument. The instantiation expands into the complete functor-code BODY and as such can be considered a particular sort of macro-expansion. Note that there is no requirement that a specific export of an argument-module must be syntax or non-syntax - it can be syntax in one instantiation and a procedure definition in another.
ARGUMENTMODULE may also be a list of the form (ALIAS DEFAULT) to allow specifying a default- or optional functor argument in case the instanation doesn't provide one. Optional functor arguments may only be followed by non-optional functor arguments.
The common case of using a functor with a single argument module that is not used elsewhere can be expressed in the following way:
(module NAME = FUNCTORNAME BODY ...)
which is the same as
(begin (module _NAME * BODY ...) (module NAME = (FUNCTORNAME _NAME)))
Since functors exist at compile time, they can be stored in import-libraries via -emit-import-library FUNCTORNAME or -emit-all-import-libraries (see Using the compiler for more information about this). That allows you to import functors for later instantiation. Internally, a functor-definition also defines a module with the same name, but importing this module has no effect. It also has no runtime code, so it is sufficient to merely import it (as opposed to using require-extension or one of its variants, which also loads the run-time part of a module).
Note that functor-instantiation creates a complete copy of the functor body.
current-module
- (current-module)syntax
This will expand to a symbol which matches the current module's name when used inside a module. If not inside a module (i.e., at toplevel), this expands to #f.
Previous: Interface to external functions and variables
Next: Types