[comp.lang.scheme] Making programs out of modules in Scheme

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/19/91)

I just received E-mail from someone who believes that Scheme as it
currently stands does not have the facilities required to build a
library out of modules.

I claim that he is wrong, that Scheme as it stands provides everything
we need to build programs out of modules that do not interfere with
each other's name space.

All it takes to do this is to define ONE globally accessible function.

I'm posting this in order to receive constructive criticism.  Please
note that of two distinct claims: (a) this is what a good module
facility for Scheme would look like, and (b) this is a simple facility
we can build with existing tools and it will (just) do the job,
I am making claim (b), not claim (a).

Here's the interface of the function.

    (library 'register ModuleName:symbol FileString:string)

	The idea is to keep (system-dependent) file names out of the
	library files themselves.  A ``master'' file, which would be 
	site dependent as well as OS dependent, maps module names to
	the file name to be given to load.  This file must be loaded
	in some site-specific manner; after that everything is portable.
	
    (library 'define ModuleName:symbol ExportName:symbol Value:any)

	A module exports things by calling this.  The value is somehow
	associated with (ModuleName,ExportName), replacing any previous
	association.

    (library 'require ModuleName:symbol)

	This is exactly like QP's ensure_loaded/1; if no such 'require
	command has ever been given before, it is recorded that the
	load has been started, and (load FileString) is done.  In a
	system where you can specify the environment to load, one would
	load into user-initial-environment or its equivalent.

    (library 'value Modulename:symbol ExportName:symbol)

	This returns the value 'defined for (ModuleName,ExportName).
	It is an error to ask for a symbol which was not 'defined.
	If the Modulename has not been loaded, it does a 'require
	first.  (Which doesn't _quite_ make 'require redundant.)

Here's how it would be used.  A library file would look like

	(let (-local variables-)
	    (library 'require 'mod1)	; this uses mod1
	    (library 'require 'mod2)	; this uses mod2
	    (let ((foo (library 'value 'mod1 'foo))
		  (bar (library 'value 'mod2 'fred))
		  ...)			; other such imports
		(define ...)		; define local and
		(define ...)		; exported things
		...			; in any mix
		(library 'define 'thismod 'ugh snark)
		(library 'define 'thismod 'zoo boojum)
		...			; other exports
		'thismod))

That's it.  The entire file would be one let-expression.

The implementation of (library mod . rest) is so trivial that it is left
as an exercise for the reader.  This suffices to show that the LANGUAGE
permits the construction of modular libraries.  Now, whether any
particular Scheme _implementation_ handles such files well is another
matter.
-- 
Q:  What should I know about quicksort?   A:  That it is *slow*.
Q:  When should I use it?  A:  When you have only 256 words of main storage.

markf@zurich.ai.mit.edu (Mark Friedman) (06/19/91)

In article <6384@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

   I just received E-mail from someone who believes that Scheme as it
   currently stands does not have the facilities required to build a
   library out of modules.

   I claim that he is wrong, that Scheme as it stands provides
   everything we need to build programs out of modules that do not
   interfere with each other's name space.
   ...
       (library 'register ModuleName:symbol FileString:string)
       ...
       (library 'define ModuleName:symbol ExportName:symbol Value:any)
       ...
       (library 'require ModuleName:symbol)
       ...
       (library 'value Modulename:symbol ExportName:symbol)
   ...

   The implementation of (library mod . rest) is so trivial that it is
   left as an exercise for the reader.

Without LOAD it is not so trivial. LOAD is not in IEEE standard Scheme
(at least not in my draft).

   This suffices to show that the LANGUAGE permits the construction of
   modular libraries.

Certainly the language permits it. The question is whether the
standard language has sufficient capabilities to build a module
system.  Without LOAD or EVAL I don't see how to do it.

-Mark
--

Mark Friedman
MIT Artificial Intelligence Lab
545 Technology Sq.
Cambridge, Ma. 02139

markf@zurich.ai.mit.edu

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/21/91)

In article <MARKF.91Jun19104522@montreux.ai.mit.edu>, markf@zurich.ai.mit.edu (Mark Friedman) writes:
> Without LOAD it is not so trivial. LOAD is not in IEEE standard Scheme
> (at least not in my draft).

Excuse me while I scream quietly ...... ah, that's better.
Right.  It's in the R^nRS, but not the p1178 draft I have either.

> The question is whether the standard language has sufficient capabilities
> to build a module system.  Without LOAD or EVAL I don't see how to do it.

Ok, so standard Scheme + LOAD is sufficient to build a module system
(if you want nested modules, it doesn't get a lot harder),
but LOAD is not standard.  Indeed, it appears that a standard Scheme
system would be within its rights to demand that it be presented with
the whole program in one file.

-- 
I agree with Jim Giles about many of the deficiencies of present UNIX.

jaffer@zurich.ai.mit.edu (Aubrey Jaffer) (06/22/91)

In article <507@data.UUCP> kend@data.UUCP (Ken Dickey) writes:
   ... I have yet to see an
   implementation of Scheme lacking either EVAL or LOAD, so I think that
   we can safely say that they are part of Scheme--even if not part of
   the minimalist IEEE standard.  I agree that it would be much better to
   have the interface (call form) of EVAL and LOAD as a standard--not to
   mention ERROR!  LOAD is certainly in R^3RS (and I suspect R^4RS, but I
   don't have a copy in front of me 8^).

EVAL is not part of scm2 (available from altdorf.ai.mit.edu).  Adding
EVAL would prevent scm2 from memoizing the locations of variable
references.

   It would be good if in R^5RS there was agreement on what the call
   forms for EVAL, ERROR, etc. are if they are provided.

There is no consensus on which environment to use for EVAL and how to
specify it.  LOAD is subject to the same environment problems.
----------------
I think that Scheme is seriously deficient in it's lack of library
facility.  One could have system varibles *library-prefix* and
*library-suffix* in order to transform library names to absolute
filenames or have a routine to do this.  This is not sufficient with
an interpreted system, however.  In an interpreted system I want to be
able to run my program from a directory other than the one where the
Scheme source for the program is stored.  So I need some sort of local
library as well.

The problem is complicated even more by the desire to have non-scheme
files accessible in the local library directory as well.  This problem
is NOT addressed the module proposals.

This library problem is present in all modern computer languages.  I
have not seen any solutions I like (although I haven't surveyed the
literature).  I would really like to see Scheme solve this one RIGHT
instead of in an ad-hoc manner.

davis@passy.ilog.fr (Harley Davis) (06/24/91)

In article <6384@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes:

   I just received E-mail from someone who believes that Scheme as it
   currently stands does not have the facilities required to build a
   library out of modules.

   I claim that he is wrong, that Scheme as it stands provides everything
   we need to build programs out of modules that do not interfere with
   each other's name space.

   All it takes to do this is to define ONE globally accessible function.

It's true that in Scheme, CommonLisp, or many other languages you can
get a sort of poor man's module facility using conventions like those
described.  However, they do not support many of the features that
modules are generally thought to give you:

 * Compile-time or load-time resolution of bindings.
 * Renaming on import or export.
 * Ease of module integration. (In all of the proposed mechanisms,
   referencing a local function is quite different from referencing an
   imported function.  If this is not the case, it is impossible to
   hide local names.)

In addition, a language which requires compile-time runtime features
-- eg, macros -- should distinguish between compile-time requirements
and runtime requirements, to minimize application size.  A module
system for such a language should support this distinction.
CommonLisp does this with EVAL-WHEN, but modules with explicit
compilation and runtime requirements provide a more comprehensible
way.

In general, extensions or conventions which provide major language
features are frustrating, because implementations cannot leverage off
them to increase program and programmer efficiency.  This is why, for
example, EuLisp and Le-Lisp version 16 provide modules as a language
feature rather than as separate add-ons.

However, for Scheme, a smaller language with more limited goals, the
suggested approaches may make sense.  Also, intermediate approaches
like the "modules" of C can be useful without incurring much cost to
the language implementor.

-- Harley Davis
--
------------------------------------------------------------------------------
nom: Harley Davis			ILOG S.A.
net: davis@ilog.fr			2 Avenue Gallie'ni, BP 85
tel: (33 1) 46 63 66 66			94253 Gentilly Cedex, France

markf@zurich.ai.mit.edu (Mark Friedman) (06/24/91)

In article <507@data.UUCP> kend@data.UUCP (Ken Dickey) writes:

   >markf@zurich.ai.mit.edu (Mark Friedman) writes:

   >The question is whether the standard language has sufficient
   >capabilities to build a module system.  Without LOAD or EVAL I
   >don't see how to do it.

   I think that you can lighten up a bit here Mark.

I certainly didn't mean to be heavy, in fact, I am not particularly
upset about not having LOAD or EVAL in the standard. I was just making
a technical response to Richard O'Keefe's claim of being able to write
a file oriented module system. It would be very nice if some future
version of the RnRS and thence the IEEE standard specified such a
beast. 

   PS: As to doing a module system without LOAD, you can always take the
   maximalist approach:

   ...
   (define suzie-module
     (let ()
       (define (fact n) (let loop ((n n)(a 1)) 
	   (if (< n 2) a (loop (- n 1) (* n a)))) )
       (define moby-quux ...)
       ...
       (module fact moby-quux ...)
   ) )

   ...
   (define fact (suzie-module 'fact))

I thought that Richard's claim was about a file oriented module
system. The file loading is the hard part without LOAD or EVAL.

-Mark
--

Mark Friedman
MIT Artificial Intelligence Lab
545 Technology Sq.
Cambridge, Ma. 02139

markf@zurich.ai.mit.edu

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/25/91)

In article <DAVIS.91Jun24125452@passy.ilog.fr>, davis@passy.ilog.fr (Harley Davis) writes:
> It's true that in Scheme, CommonLisp, or many other languages you can
> get a sort of poor man's module facility using conventions like those
> described.  However, they do not support many of the features that
> modules are generally thought to give you:
> 
>  * Compile-time or load-time resolution of bindings.
>  * Renaming on import or export.

The approach I presented allowed renaming.

>  * Ease of module integration. (In all of the proposed mechanisms,
>    referencing a local function is quite different from referencing an
>    imported function.  If this is not the case, it is impossible to
>    hide local names.)

The approach I presented involved referencing local and imported functions
in EXACTLY the same way.  The only difference was in definition:
	(define foo (lambda (...) ...))		; local
	(define foo (library ... ...))		; imported
Nor did the things transported have to be functions.

> In addition, a language which requires compile-time runtime features
> -- eg, macros -- should distinguish between compile-time requirements
> and runtime requirements, to minimize application size.

This has no relevance to Standard or R**3.99 Scheme, which was my sole
(and in the case of the Standard, incorrectly recalled) interest.
-- 
I agree with Jim Giles about many of the deficiencies of present UNIX.

ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (06/25/91)

In article <MARKF.91Jun24110138@montreux.ai.mit.edu>, markf@zurich.ai.mit.edu (Mark Friedman) writes:
> In article <507@data.UUCP> kend@data.UUCP (Ken Dickey) writes:
>>   I think that you can lighten up a bit here Mark.
> I certainly didn't mean to be heavy,

I think Friedman's response was exactly what was called for.  Sigh.

> I thought that Richard's claim was about a file oriented module
> system. The file loading is the hard part without LOAD or EVAL.

It was, and it is.  I have seen at least one *real* proposal for adding
modules to Scheme, and no doubt there are others.  The question of
interest to me is "what can we do while we're waiting"?

Surely there is only one sensible environment for (LOAD "file") to
extend, and that is "the" top-level environment?  (Or the current
"locale", if you have locales.)

-- 
I agree with Jim Giles about many of the deficiencies of present UNIX.