You are looking at historical revision 5742 of this page. It may differ significantly from its current revision.

lexmod

Introduction

The name 'lexmod' is short for 'lexical module'.

-- This module system is based on Lexical Capture --

Documentation

lexmod

[syntax] (lexmod MODULE-NAME EXPORT-LIST BODY) -> lexmod

Create a lexmod with the given MODULE-NAME, EXPORT-LIST, and BODY.

The EXPORT-LIST should have a form of:

  (export EXPORT-CLAUSE ...)

Each EXPORT-CLAUSE should have a form of:

SYMBOL : Export SYMBOL as defined from BODY

or

(as EXPORTED INTERNAL) : Export EXPORTED as what INTERNAL was defined as in BODY.

The definitions in BODY are evaluated as in a lambda body.

Qualified module names have the form: (MODULE 'NAME)

Modules within modules are perfectly possible. Qualified names in nested modules can end up becoming a little hairy though -- ((MODULE 'SUBMODULE) 'SUBMODULE-BINDING) --.

In a later version, this might be made nicer.

define-lexmod

[syntax] (define-lexmod NAME EXPORTS BODY)

Convenience for

  (define NAME (lexmod NAME EXPORTS BODY))

lexmod-exports?

[procedure] (lexmod-exports? LEXMOD NAME) -> boolean

Takes a LEXMOD and a symbol NAME and returns #t iff the lexmod exports the symbol, or #f otherwise.

let-imports

let*-imports

[syntax] (let-imports  ((MODULE IMPORT-CLAUSE ...) ...) BODY)
[syntax] (let*-imports ((MODULE IMPORT-CLAUSE ...) ...) BODY)

Import names from lexmods in a local context. Each MODULE should evaluate to a lexmod; each IMPORT-CLAUSE should have a form of:

SYMBOL : Import SYMBOL from the corresponding module exactly

or

(as IMPORTED EXPORTED) : Import EXPORTED from the corresponding module, but rather bind IMPORTED to its value.

let*-imports is like let-imports, but it binds each MODULE clause sequentially, whereas let-imports binds in an order such that each MODULE is evaluated in the environment of the context of the let-imports form.

That is, let-imports might be defined to expand

    (let-imports ((MOD1 A (as B Z) C) (MOD2 D (as E Y) F)) BODY)

to

    (let ((A (MOD1 'A)) (B (MOD1 'Z)) (C (MOD1 'C))
          (D (MOD2 'D)) (E (MOD2 'Y)) (F (MOD2 'F)))
      body)

and let*-imports might expand

    (let*-imports ((MOD1 A (as B Z) C) (MOD2 D (as E Y) F)) BODY)

to

    (let-imports   ((MOD1 A (as B Z) C))
      (let-imports ((MOD2 D (as E Y) F))
        BODY))

define-imports

[syntax] (define-imports (MODULE IMPORT-CLAUSE ...) ...)

Like let-imports, but expand to a set of define, rather than bind locally a bunch of variables. Each IMPORT-CLAUSE has the same syntax & semantics as with let-imports.

interface

[syntax] (interface NAME EXPORT ...) -> interface

Create an interface with the given EXPORT ... and NAME symbol.

define-interface

[syntax] (define-interface NAME EXPORT ...) -> interface

Convenience for

  (define NAME (interface EXPORT ...))

interface-exports?

[procedure] (interface-exports? INTERFACE NAME) -> boolean

Does INTERFACE contain NAME in its export list?

lexmod-satisfies?

[procedure] (lexmod-satisfies? LEXMOD INTERFACE) -> boolean

Does LEXMOD export everything that INTERFACE demands it to?

lexmod-fulfills?

[procedure] (lexmod-fulfills? LEXMOD [INTERFACE | (export NAME ...)] ...) -> boolean

Performs both 'lexmod-exports?' and 'lexmod-satisfies?'.

lexmod?

[procedure] (lexmod? OBJECT [INTERFACE | (export NAME ...)] ...) -> boolean

Is the OBJECT a lexmod, that also fulfills the optional export criteria?

lexmod-open

[procedure] (lexmod-open LEXMOD) -> boolean

Binds the toplevel variables with the same name as the exported symbols of LEXMOD to their corresponding value.

compound-lexmod?

[procedure] (compound-lexmod? OBJECT) -> boolean

Is the OBJECT a lexmod?

compound-lexmods

[procedure] (compound-lexmods LEXMOD) -> list

Returns the components of a compound-lexmod as a list-of LEXMOD, or '().

compound-lexmod

[procedure] (compound-lexmod [NAME] LEXMOD ...) -> lexmod

Returns a new lexmod, exporting the union of the LEXMOD parameter's exports. The set of exports must be dis-joint otherwise an error is raised. Unless a name is provided the name of the new lexmod is the symbol with printname "+<lLEXMOD-1 name>+...+<LEXMOD-N name>".

functor

[syntax] (functor NAME ((<lexmod-arg> INTERFACE) ...) EXPORT-LIST BODY)
[syntax] (functor ((<lexmod-arg> INTERFACE) ...) EXPORT-LIST BODY)

Functors are functions that take lexmods and return lexmods. The functor macro ensures that the lexmods satisfy the interfaces.

EXPORT-LIST is like the list of exports in lexmod. NAME will be the name of the lexmod the functor returns; it defaults to the symbol _anonymous_.

The functor takes a parameter list (PARAM-NAME ...) and each PARAM-NAME is checked to satisfy its respective interface when the functor is applied. Functor application is ordinary function application.

Conventions

modules
@MODULE-NAME
interfaces
INTERFACE-NAME-interface
functors
@make-NAME

Known problems

There are only four (known) problems with this module system:

1. It doesn't support macros; this module system is purely run-time, while macros are macro-expand- or compile-time-only.

2. There can be no initialization code scattered throughout the module, so you can't do, for instance:

  (lexmod @foo (export bar baz quux)
    (define bar #f)
    (define (baz stuff) body)
    (set! bar (baz mumble))
    (define quux (zot bar)))

because then the set! expression unsets the fact that it's a definition context in the rest of the lexmod expression, and so that define expression below it is in an expression context, not a definition context, which won't work very well. Instead put all initialization after all definition -- for example:

  (lexmod @foo (export bar baz quux)
    (define bar #f)
    (define (baz stuff) body)
    (define quux #f)
    (set! bar (baz mumble))
    (set! quux (zot bar)))

(syntax-case's module system exhibits this problem as well, so hah!)

Riastradh has written a macro for giving top-level semantics to local scopes, but it has one problem: those local bodies can't contain macros that expand to define and work right, which is why he hasn't put it in the lexmod distribution yet.

3. Because each subform is wrapped inside a single let, the definitions are ordinary internal defines, so they're equivalent to a letrec -- i.e. they aren't evaluated sequentially, and they don't have _all_ the properties of top-level defines (for example, they're not equivalent to set! if the variable is already defined) --.

These two could be fixed by wrapping every form in a lexmod expression with let, but that would cons a closure for every expression, which would be rather annoying.

4. The export list should be able to be an interface as created with the interface macro, not just with (export ...). Due to this problem, I'll probably change interfaces to use macro magic that lets them be both macro-expand-time and run-time values so that the lexmod macro can use them, and they can also be used as ordinary run-time values.

Author

Taylor Campbell, packaged for CHICKEN by felix winkelmann

Kon Lovett, extensions

History

1.0
Added compound queries, made interface disjoint
009
extensions
008
initial release as a CHICKEN extension

License

Copyright (C) 2003, 2004 Taylor Campbell
All rights reserved.
    You may use and redistribute this in source code or binary form as
you like, as long as in redistribution you include the above copyright
notice, this licence, and the following disclaimer.  Modifications are
requested, but not required, to be sent back to the author.  Any
modification must give credit to the original author, however, and be
licensed under this licence.  You may NOT distribute the author's email
address in any way, shape, or form, except in a list of credits, where
the email address AS DISPLAYED ABOVE may be used.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.