Version: 4.2.1

15.3 Scripting Evaluation and Using load

Historically, Scheme and Lisp systems did not offer module systems. Instead, large programs were built by essentially scripting the REPL to evaluate program fragments in a particular order. While REPL scripting turns out to be a bad way to structure programs and libraries, it is still sometimes a useful capability.

Describing a program via load interacts especially badly with macro-defined language extensions [Flatt02].

The load function runs a REPL script by reading S-expressions from a file, one by one, and passing them to eval. If a file "place.scm" contains

  (define city "Salt Lake City")
  (define state "Utah")
  (printf "~a, ~a\n" city state)

then it can be loaded in a REPL:

  > (load "place.scm")

  Salt Lake City, Utah

  > city

  "Salt Lake City"

Since load uses eval, however, a module like the following generally will not work – for the same reasons described in Namespaces:

  #lang scheme
  (define there "Utopia")
  (load "here.scm")

The current namespace for evaluating the content of "here.scm" is likely to be empty; in any case, you cannot get there from "here.scm". Also, any definitions in "here.scm" will not become visible for use within the module; after all, the load happens dynamically, while references to identifiers within the module are resolved lexically, and therefore statically.

Unlike eval, load does not accept a namespace argument. To supply a namespace to load, set the current-namespace parameter. The following example evaluates the expressions in "here.scm" using the bindings of the scheme/base module:

  #lang scheme
  (parameterize ([current-namespace (make-base-namespace)])
    (load "here.scm"))

You can even use namespace-anchor->namespace to make the bindings of the enclosing module accessible for dynamic evaluation. In the following example, when "here.scm" is loaded, it can refer to there as well as the bindings of scheme:

  #lang scheme
  (define there "Utopia")
  (define-namespace-anchor a)
  (parameterize ([current-namespace (namespace-anchor->namespace a)])
    (load "here.scm"))

Still, if "here.scm" defines any identifiers, the definitions cannot be directly (i.e., statically) referenced by in the enclosing module.

The scheme/load module language is different from scheme or scheme/base. A module using scheme/load treats all of its content as dynamic, passing each form in the module body to eval (using a namespace that is initialized with scheme). As a result, uses of eval and load in the module body see the same dynamic namespace as immediate body forms. For example, if "here.scm" contains

  (define here "Morporkia")
  (define (go!) (set! here there))

then running

  #lang scheme/load
  (define there "Utopia")
  (load "here.scm")
  (printf "~a\n" here)

prints “Utopia”.

Drawbacks of using scheme/load include reduced error checking, tool support, and performance. For example, with the program

  #lang scheme/load
  (define good 5)
  (printf "running\n")

DrScheme’s Check Syntax tool cannot tell that the second good is a reference to the first, and the unbound reference to bad is reported only at run time instead of rejected syntactically.