[comp.lang.scheme] Declarative Load and multiple environments

jaffer@gerber.ai.mit.edu (Aubrey Jaffer) (03/06/91)

`Load' as defined in Revised^3.99 Report on the Algorithmic Language
Scheme [Draft August 31, 1989] has several problems owing to its
dynamic nature:

1) `load' opens a back door to eval:
	(define global-eval-return #f)
	(define (eval frob)
	  (call-with-output-file "tmp1"
	    (lambda (file)
	      (write (list 'set! 'global-eval-return frob) file)))
	  (load "tmp1")
	  global-eval-return)

2) `load' does not specifiy the lexical environment in which the forms
	are evaluated. 

3) A scheme program that includes a `load' in it will cause the run
	time system to need to include either an interpreter or
	compiler.

Making `load' a derived expression rather than a procedure solves all
these problems.  I will call it `include' in order to avoid name
confusion.  The Report entry would be something like:
------------------------------------------------------------------------
(include filename)					essential syntax

Filename should be a string naming an existing file containing Scheme
source code <expression1> <expression2> ....  The <expression>s are
evaluated as though contained in a surrounding begin form
(begin <expression1> <expression2> ...) in the lexical environment.

Some implementations may require that if the file specified by
filename contains definitions and the include loading it is inside a
<body> then the the definitions must be at the beginning of the file
and the include must be before any expressions in the <body> which are
not includes or definitions.

If the contents of the file specified by <filename> change during
execution of the program then the actions of include are not specified.

Include does not affect the values returned by current-input-port and
current-output-port.  Include returns the value of the last <expression>
in the file.
------------------------------------------------------------------------

In the Derived Expression Types section would be the rewrite rule:

(include <filename>)
= (begin <file-expression1> <file-expression2> ...)

A macro for include would be:

(define-macro include
  (lambda (filename)
    (cons 'begin
	  (call-with-input-file filename
	    (lambda (file)
	      (let next ((r (read file)))
		(if (eof-object? r) '()
		    (cons r (next (read file))))))))))

The above problems are solved:

1) Expressions cannot be evaluated (included) which are not known at
	compile time.

2) The lexical environment of the expressions in the file is the
	lexical environment of the (include <filename>) form.

3) In a compiled only system all the files included are required to be
	present at compile time.  A runtime eval is not needed.

This definition of include will satisfy most existing use of load.  In
addition, it allows definitions to be loaded into a lexical
environment without affecting the global environment:

(define (add-with-modular-arithmetic . args)
  (include "modular-arithmetic-functions.scm")
  (apply + args))

Incompatability issues:

1) Include specifies a return value.  Load does not.

2) Include does not make sense on non-source files.  Load allows
	implementation dependent behavior on non-source files.

For an interpreted system, an interesting extension is to have
(include #f) call the read-eval-print loop in the current lexical
environment.  If the interpreter does not care about mixing defines
and expressions this would implement separate name spaces which
several people have asked for.

Alan@lcs.mit.EDU (Alan Bawden) (03/07/91)

   Date: 5 Mar 91 16:03:02 GMT
   From: Aubrey Jaffer <jaffer@gerber.ai.mit.edu>
   ...
   Making `load' a derived expression rather than a procedure solves all
   these problems.  I will call it `include' in order to avoid name
   confusion.  The Report entry would be something like:
   ------------------------------------------------------------------------
   (include filename)                                      essential syntax

   Filename should be a string naming an existing file containing Scheme
   source code <expression1> <expression2> ....  The <expression>s are
   evaluated as though contained in a surrounding begin form
   (begin <expression1> <expression2> ...) in the lexical environment.
   ...

I think we talked about this idea a bit at Snowbird.  I believe the major
objection was that <filename> isn't an arbitrary expression.  That is,
people wanted to be able to -compute- which file to load.  (Please note:
I'm not endorsing this position, I'm just trying to remember the history.)

There may also have been concerns that a restriction like:

   If the contents of the file specified by <filename> change during
   execution of the program then the actions of include are not specified.
   ...

(which does seem necessary in order to make sense of the idea) may raise
some issues about the differences between read-time, macroexpand-time,
syntax-time, compile-time, load-time and run-time that Scheme has so far
avoided addressing.  (Again this is not my opinion, just my memory of
the history.)