scheme shell

From: Olin Shivers <>
Subject: Scheme 48 module system
Newsgroups: comp.lang.scheme.scsh
Date: 09 May 1998 23:08:27 -0400
Organization: Artificial Intelligence Lab, MIT
Message-ID: <>

   From: Brian Denheyer <>
   The define structure part I understand, I figured that out pretty
   early on.  What's got me stuck is how you actually use the structure
   you've created.  I keep wanting to do something like (yes you can tell
   I'm a python guy) :

     (import structure)

     (use things which were exported from structure, maybe with some type
   of qualifier...)

   So after some muddling around I ended up with :

     #! /usr/local/bin/scsh \
     -lm module-def.scm -o module -s

     (code which uses things in module... )

Brian, you are on the right track here. I'll describe things in more detail 

   So other than setting up the script in this manner I really don't
   understand how you "import" things from the structures which you've
   defined.  I have the paper which talks all about the module system and
   it still it eludes me.

   I'm thinking that maybe the "code which uses..." part should actually
   be in the define-structure, but then how would I run it ?  Just
   loading the module definition doesn't work, e.g.

     scsh -s module-def.scm 

   results in :

     Error: undefined variable
	    (package user)

That's right -- it doesn't work. Because module-def.scm doesn't contain Scheme.
It contains expressions from the module-definition language -- expressions
of the form

    (define-structure int-funs (exports factorial ack fib)
      (open scheme)
      (begin (define (factorial n) ...)
	     (define (ack n) ...)
	     (define (fib n) ...)))

If you look in the R4RS spec, you won't see the DEFINE-STRUCTURE form
defined -- that's what I mean by "it's not Scheme."

The problem is that if you just say
    scsh -s module-def.scm
then scsh is going to load module-def.scm into the user package. Well,
the user package opens (or "imports", if you like) bindings for all the
standard Scheme things (variables like CAR, DISPLAY, and syntax keywords
like LAMBDA, QUOTE, DEFINE), plus all the scsh stuff like FORK and EXEC.
But the user package *doesn't* have any bindings for DEFINE-STRUCTURE.
So you'll lose -- the (define-structure int-funs ...) expression won't
make sense.

However, there's another package, the config package. This package is
very different -- it *doesn't* have bindings for CAR, CDR, APPEND, LAMBDA, 
etc., but it *does* have DEFINE-STRUCTURE available. You *can* load an 
expression written in the module language into this package. That's what
    scsh -lm module-defs.scm
does. It's also what happens if you say
    ,config ,load module-defs.scm
at the scsh prompt. If module-defs.scm contains the structure
definition I sketched out above, then after you do this, your scsh has
a new package available. This package is bound to the variable int-funs
in the config package. Here, let me say that again. In the user package,
you still just have CAR, CDR, APPEND, LAMBDA, COND and friends. In the
config package, you have the keyword DEFINE-STRUCTURE, plus a new
variable binding -- the variable INT-FUNS is bound to your new package.
The config package is a package that contains other packages. For example, 
it contains a variable PP, which is bound to the pretty-printer package, 
which itself exports the variable P, which is the pretty-printing procedure. 
In the config package, you will also find packages like SCSH and SCHEME.

OK, so far so good. We have our new package loaded. Notice that our
package definition
    (define-structure int-funs (exports factorial ack fib)
      (open scheme)
      (begin (define (factorial n) ...)
	     (define (ack n) ...)
	     (define (fib n) ...)))
opened the SCHEME package. What does this mean? Well, it means that
the package system looks in the config package for a variable named
SCHEME. This guy's value is itself a package -- the one defining and
exporting all the standard Scheme bindings. The package system grabs
all these bindings and makes them available to int-funs -- so the
forms in int-fun's (BEGIN ...) clause get to use things like LAMBDA,
IF, +, etc. If int-fun had contained a
    (FILES foo.scm bar.scm)
clause, the source in these files would have been able to reference these
Scheme bindings, too.

Now that we have our int-fun package defined, we'd like to use the functions
it defines. There are a couple of ways we could do this.
- From the scsh prompt, we could say
    ,in int-funs
  This would "move" the scsh interpreter into the package int-funs
  (which it would find by looking in the config package, where all the
  packages are defined). Now we can see all the stuff in this package.
  Not just the three bindings we exported (factorial, ack & fib), but
  all the unexported internals, as well. We can redefine stuff, try
  things out, and so forth, all in our package.

- From the scsh prompt, we could say
    ,open int-funs
  This would import int-funs' exported bindings (factorial, ack & fib)
  into our current package (which is probably the user package). Now we
  can reference these three bindings from the user package.

- If we are doing a script, we could say something like this:

    #!/usr/local/bin/scsh \
    -lm foo-module.scm -o int-funs -s

    (display (factorial (string->number (argv 1))))
    (write-char #\newline)

 The command line says: (1) load foo-module.scm into the config package
 (so it better contain source written in the module language), (2) open
 the package int-funs in the current package (which is by default
 the user package, though we could have changed it with the -m switch),
 (3) load our script and then exit. Voila -- your script got access to your
 little package of integer functions.

I think S48's module system is an incredibly cool mechanism. It's more
than just allowing you to define modules of Scheme code. You get to
define complete sets of language bindings, including all the syntax
keywords like LAMBDA and IF and so on. If your package opens the SCHEME
package, your package can be defined in terms of Scheme; no sweat. But
if you *don't* open the SCHEME package, you don't get any of it! You can 
instead open up some other *completely different* set of keywords and base 
function bindings.

Can't do that in C.

P.S. It's an undocumented feature of S48's module system, but you aren't
even restricted to Scheme's s-expression syntax when you use Kelsey and
Rees' module system. There's an extra clause in the DEFINE-STRUCTURE
form that lets you specify the parser function used to parse the files
named in the FILES clause. Now *that's* a general module system!