[R6RS] Modules

Manuel Serrano Manuel.Serrano
Wed Nov 3 00:57:26 EST 2004


> I've attached a---from my point of view---minimal revision of Kent's
> proposal to make me happy.  Manuel and, I think, Richard, agree with
> the changes and their rationale, but may want to make more changes.
This is true.

Here are my notes:

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
Here are my current thoughts about the module proposal. One more time,
I think that, at least for me, we are pretty close to an
agreement. I'm nearly okay with the Kent's proposal. I have some point
of disagreement that I will detail in the mail. First of all, let me
state that, of course, we can discuss everything. The position I'm
taking today only reflect my current understanding of the Kent's
proposal. It may evolve, it may change.

In summary here are my 5 points of disagreement. 

1- get rid of the language parameter.

2- split the module specification and its body.

3- get rid of the association module/file.

4- change the semantics of the evaluation of the top level forms.

5- add type exportations.

Before detailing these five points here are some general thoughts:

* I think that the current proposal makes a high pressure on the compiler 
  or the programming environment. Imposing the a module is macro-expanded
  before being imported is a real constraint. Tools relying on purely
  syntactic analysis won't be able to do a good job. Tools relying on file
  stamping for building project (make) won't either be able to do a good job.

* I think that we should design a module system that is independent of
  any operating system. Granted the r6rs modules, one should be able to make
  several possible implementations. One using a browser ? la Smalltalk,
  one using a Windows like IDE. A last one respecting the Unix philosophy.
  As you can imagine I'm strongly attached to this last possibility. In
  particular, it means that for me it is of the highest important to:
   - be able to use make (the very Unix make command, not any replacement)
   - an Emacs like editor
   - Grep like tools

  I don't want the module system to prevent me from writing programs like
  I have been writing them for decades (yes, yes, decades, it makes more
  than 25 years that I'm addicted to programming :-).

Now let me zoom in the 5 previous points:

%*---------------------------------------------------------------------*/
1- get rid of the language parameter.

I really don't see the necessity of the language specification. As far
as I understand, with the language argument, one should write:

   (module foo r6rs ...foobar...)

without it, he should write:

   (module foo (require r6rs) ..foobar...)

Is it really for saving 10 characters that we are introducing this 
irregularity? Why calling a special MODULE-SPEC a language and writing
it without its REQUIRE syntax? What is the interest of this?

Note: In addition, I think that there is a incoherence in the current
proposal. A MODULE-SPEC imports the bindings of a module. It does not
important its syntaxes (or I don't understand the verbing of
ALSO-FOR-SYNTAX of the REQUIRE specification).

%*---------------------------------------------------------------------*/
2- I really don't like the closing of the MODULE form. I don't like to
put the definition inside the definition of the module. I would really
push in favor of a form where the module only contains the require and
provide forms, the definitions being written elsewhere. That is, instead
of something such as:

Solution A:

   (module foo (require r6rs)
     (provide foo)

     (define (foo x) x)
     )

I would really prefer:

Solution B:

   (module foo (require r6rs)
     (provide foo))

   (define (foo x) x)

I'm strongly in favor of this change for the following reasons:

i- it is far easy to pretty print and to edit Solution B. It does not waste
space in the left column of your editor. It gets rid of the last ugly
closing parenthesis of solution A.

ii- it prevents from macro-expansing PROVIDE and REQUIRE forms. I
think that this is a decision we must take because I think that it
eases the understanding of program. I think that it is a very good
property to be able to understand what is required and what is
exported by a module with only one glance.

iii- The solution B enables splitting a module across several files. In Bigloo
I frequently use this. When I have to make a library from an 
as-portable-as-possible Scheme source file foo.scm, I write a module
specification in a foo.bgl file and I invoke the compiler with a command line
that looks like:

  bigloo foo.bgl foo.scm

In this way, I don't have to edit nor to change foo.scm for making it
Bigloo compliant. 

If you want a more "general" example, imagine a very old but very interesting
Scheme source file "interesting.scm" implemented in Scheme R4Rs. Imagine now
that with your favorite R6RS implementation you provide a backward 
compatibility kit. Then, with solution B you could have two files:

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
file1.scm:
(module very-interesting-r4rs-implementation (require r4rs)
  (provide ...))

interesting.scm:
<your very interesting r4rs code>
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----

This way, you don't have to edit the old source file. It is directly
usable. 

%*---------------------------------------------------------------------*/
3- I definitively think that we should not permit module definitions to
contain module/file association. I think that this is a bad idea. It prevents 
from moving the files on the disk. Potentially, it forces user to update their 
sources when they only rename files. Imagine the following:

   (module a-library (require r6rs)
      (require (another-library from "directory1/another-library.scm")))
   ...


For a reason or another "directory1" is changed to "directory2". The
module a-library has to be updated. Imagine someone using time stamp
for remembering different versions. The module a-library will be
stamped with a new date even though it has not been changed.

In conclusion, the association module/file should NOT be specified in
the language. It's up to the implementation to provide a mechanism for
deciding the association. For a clause such as (require FOO) an implementation
could decide that the module is located in the file FOO.scm, FOO.your-prefix,
your-directory/FOO.your-prefix, ... It has nothing to do with the definition
of the language itself.

%*---------------------------------------------------------------------*/
4- change the semantics of the evaluation of the top level forms.

Simple compilers for Scheme r6rs should be able to deliver good
performance applications. In other words, I think that we must ensure
that implementing a reasonably good compiler for r6rs is not a process
that requires 10 years of research and sophisticated development.

One step in this directory is to change a little bit the evaluation of
the top-level forms. I would suggest that in the process of
initializing modules, we specify that all the DEFINE forms are
executed, in sequence, before the other top-level forms. That is, the
two following modules are equivalent:

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
(module foo1 (require r6rs))

(define (foo x) x)
(foo 1)
(define (bar x) x)
(set! foo x list)
(foo 2)
(bar 2)
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
(module foo1 (require r6rs))

(define (foo x) x)
(define (bar x) x)
(foo 1)
(set! foo x list)
(foo 2)
(bar 2)
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
  
I think that it really makes compilers life much easier.

%*---------------------------------------------------------------------*/
5- add type exportations.

We need to define new types. For now, we have only discussed records
but when the time of monomorphic vectors, classes and foreign
interface, ... is come we will see other opportunities for defining
types. For now, let's focus on the records.

In a recent mail, Marc suggested that the pattern matching should be aware
of the defined records. I agree on that. This means that the following two
modules should work together:

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
(module mod1 (require r6rs)
  (provide point point3d))

(define-record point ...)
(define-record point3d ...)
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----

-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
(module mod2 (require r6rs)
  (require mod1))

(define (foo x)
  (match-case x
    (#{point 1 2} 'point)
    (#{point3d 4 5 6} 'point3d)))
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----

This means that the form (provide point point3d) clearly means that the
things that are exported there are types. I think that this poses a problem
because we have actually two spaces of values (the Scheme values and
the types) and the PROVIDE clause is not able to distinguishes between the
two. I think that we have the same problem with syntaxes. In order to
distinguish between values, syntax and types, I think that we should provide 
three different PROVIDE clause:

<provide-form> -> (PROVIDE <provide-spec>*)
<provide-spec> -> <identifier>
                | (<identifier> :SYNTAX) 
                | (<identifier> :TYPE) 
-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----


-- 
Manuel


More information about the R6RS mailing list