[R6RS] modules?

Matthew Flatt mflatt
Tue Apr 13 23:10:59 EDT 2004


I've struggled all day to reply to Kent's and Mike's message, and the
best I can do seems too technical for this stage of the discussion.

I'd prefer to concentrate on a top-level module system at first, and
leave internal modules to a second round. I think that MS modules are
more important for portable and modularized code, whereas SC modules
are more useful for building macros.

But here goes, anyway.

----------------------------------------

At Tue, 13 Apr 2004 18:15:49 +0200, Michael Sperber wrote:
> Personally, I think that MzScheme-style modules and Chez-style modules
> are largely orthogonal and should not be conflated.  (... and the use
> of the MODULE keyword by both and the superficial similarities is a
> recurring source of confusion over on c.l.s.)

This was my first impression, too, but now I'm not so sure.

The more I think about it, the more MS and SC seem analogous to
top-level `define' and internal `define'. Semantically, the two
`define's are not quite the same. But they're very similar, the
reflecting the similarity with a single keyword can make code more
readable (i.e., `define's everywhere, instead of a mixture of `define's
and `letrec's). The difference between the two `define's sometimes
causes trouble for language designers and programmers, but Schemers
like internal definitions enough that we put up with it.

In the same way, top-level modules and internal modules are not quite
the same (not even in Chez, currently). But a consistent syntax might
be a good idea, anyway.


> Meanwhile, have you seen that PLT Scheme comes with a library called
> "package.ss" that implements essentially what's MODULE in Chez?

It doesn't quite qualify as an implementation of SC, but it can express
many of the same things (everything the POPL SC paper, certainly).

For whatever reason, this library hasn't seen much use --- and not due
to the ways that `package' fails to be SC, I think. In any case, I
don't have a good feel for how SC gets used, even though I think I
understand the semantics.

Kent - can you point us to code that uses SC (with nested modules)?
Comparing SC code and MS code[*] may help us get a sense of the
similarity and differences. I think it would help me, anyway.

 [*] Lots of MS code is available in plt/collects in the DrScheme
     distribution.

----------------------------------------

Meanwhile, I'll try to comment on the semantic level, though it brings
us to phases.

The difference between top-level and internal modules is tied up with
phases (expansion versus run-time). In Chez:

  ; Top level
  (module m (f) (define (f x) (list x)))
  (define-syntax s1 (lambda (stx)
                      (import m)
                      (syntax-case stx ()
                        [(_ v) (f #'v)])))
  (import m)
  (define (g) (f 10))
  (s1 g) ; => '(10)

  ; Internal
  (let ()
    (module m (f) (define (f x) (list x)))
    (define-syntax s1 (lambda (stx)
                        (import m)
                        (syntax-case stx ()
                          [(_ v) (f #'v)])))
    (import m)
    (define (g) (f 10))
    (s1 g))) ;  => Error: identifier out of context f.

One way to look at it is that MS helps make top-level modules more like
internal modules, by not allowing a top-level module to automatically
span phases like `m' above. But distinguishing phases also affects
internal modules and imports...

>   * MS has a "require-for-syntax" form that makes module bindings
>     available for macro implementations at expand time; SC does not
>     but doesn't need it since one can always stick an import right
>     in the body of the macro implementation, e.g.,
> 
>         (define-syntax a
>           (lambda (x)
>             (import goodstuff)
>             <expansion code>))

The internal `import' in SC is not quite the same as a top-level
`require-for-syntax'. In SC, `import' introduces bindings that
<expansion code> might use directly (e.g., to call a function), or that
<expansion code> might use in templates, so that the expansion result
refers to binding in `goodstuff'. In other words, the `goodstuff'
bindings become available in both phases.

This is bad in the MS view, because an instance of `goodstuff' really
exists in only one phase. But the MS view isn't incompatible with
internal imports --- it just needs phase-specific `import's.

In an expander, probably the programmer would want `import' to bind in
the expansion-time environment (which is like `require-for-syntax'). To
bind in templates, the programmer would need `import-for-template'
(where `-for-template' phase-shifts in the opposite direction of
`-for-syntax').

Within an expander, I think that only `import-for-template' makes sense
for a non-top-level `module' defined in a run-time position:

   (let ()
     (module goodstuff)
     ...
     (define-syntax a
       (lambda (x)
         (import-for-template goodstuff)
          <expansion code>))
     ...)

But only `import' would make sense for a module defined in a
compile-time position:

     (define-syntax a
       (lambda (x)
         (module goodstuff)
         (import goodstuff)
          <expansion code>))

(I note that MzScheme doesn't currently have `require-for-template'.
It's on my TODO list.)

> [...] Here is my ideal merger:
> 
>   * Adopt the common features of both systems.
> 
>   * Adopt SC internal modules and top-level/internal imports.
> 
>   * Adopt SC notion of modules being scoped where they are defined
>     plus import-only, or MS notion of language being specified with
>     a way to allow the language to default to the current scope.
> 
>   * Adopt MS syntax for specifying exports and such.
> 
>   * Adopt MS file loading/searching.

This all sounds plausible to me, as long as internal imports become
phase-sensitive.

I much prefer a language declaration for top-level modules, instead of
an implicit "Scheme" top-level, but I don't think that it's a deep
issue.

> I could go either way on when module definitions and expressions are
> evaluated, although I'm concerned that by need evaluation gets us into
> some of the same problems that we have with delay/force.  

I doubt that by-need evaluation is the right thing for internal
modules, but I feel confident that it's the right thing for top-level
modules (to make phases work correctly and naturally). By analogy to
`define', maybe it's ok if the evaluation rules are slightly different
for internal and top-level modules.

----------------------------------------

You're still reading? If it made no sense, then perhaps the only
relevant opinion is in the second paragraph.

Matthew



More information about the R6RS mailing list