[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

lexically scoped Load



`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.