Chapter 14

Support Facilities

14.1  Eval and Load

(eval expr) evaluates the S-expression expr in the current namespace.34 (See section 8 and section 7.7.1.5 for more information about namespaces.)

(load file-path) evaluates each expression in the specified file using eval.35 The return value from load is the value of the last expression from the loaded file (or void if the file contains no expressions). If file-path is a relative pathname, then it is resolved to an absolute pathname using the current directory. Before the first expression of file-path is evaluated, the current load-relative directory (the value of the current-load-relative-directory parameter; see section 7.7.1.6) is set to the absolute pathname of the directory containing file-path; after the last expression in file-path is evaluated (or when the load is aborted), the load-relative directory is restored to its pre-load value.

(load-relative file-path) is like load, but when file-path is a relative pathname, it is resolved to an absolute pathname using the current load-relative directory rather than the current directory. If the current load-relative directory is #f, then load-relative is the same as load.

(load/use-compiled file-path) is like load-relative, but load/use-compiled also checks for .zo files (usually produced with compile-file; see Chapter mz:compilefile in PLT MzLib: Libraries Manual) and .so (Unix and Mac OS) or .dll (Windows) files.36 The check for a compiled file occurs whenever file-path ends with a dotted extension of three characters or less (e.g., .ss or .scm) and when a compiled subdirectory exists in the same directory as file-path. A .zo version of the file is loaded if it exists directly in the compiled subdirectory. An .so or .dll version of the file is loaded if it exists within a native subdirectory of the compiled directory, in a deeper subdirectory as named by system-library-subpath. A compiled file is loaded only if its modification date is not older than the date for file-path. If both .zo and .so or .dll files are available, the .so or .dll file is used.

Multiple files can be combined into a single .so or .dll file by creating a special dynamic extension _loader.so or _loader.dll. When such a extension is present where a normal .so or .dll would be loaded, then the _loader extension is first loaded. The result returned by _loader must be a procedure that accepts a symbol. This procedure will be called with a symbol matching the base part of file-path (without the directory path part of the name and without the filename extension), and the result must be two values; if #f is returned as the first result, then load/use-compiled ignores _loader for file-path and continues as normal. Otherwise, the first return value is yet another procedure. When this procedure is applied to no arguments, it should have the same effect as loading file-path. The second return value is either a symbol or #f; a symbol indicates that calling the returned procedure has the effect of declaring the module named by the symbol (which is potentially useful information to a load handler; see section 5.8).

While a .zo, .so, or .dll file is loaded (or while a thunk returned by _loader is invoked), the current load-relative directory is set to the directory of the original file-path.

(load/cd file-path) is the same as (load file-path), but load/cd sets both the current directory and current load-relative directory to the directory of file-path before the file's expressions are evaluated.

(read-eval-print-loop) starts a new read-eval-print loop using the current input, output, and error ports. When read-eval-print-loop starts, it installs a new error escape procedure (see section 6.7) that does not exit the read-eval-print loop. The read-eval-print-loop procedure does not return until eof is read as an input expression; then it returns void.

The read-eval-print-loop procedure is parameterized by the current prompt read handler, the current evaluation handler, and the current print handler; a custom read-eval-print loop can be implemented as in the following example (see also section 7.7.1):

  (parameterize ([current-prompt-read my-read]
                 [current-eval my-eval]
                 [current-print my-print])
    (read-eval-print-loop))

14.2  Exiting

(exit [v]) passes v on to the current exit handler (see exit-handler in section 7.7.1.9). The default value for v is #t. If the exit handler does not escape or terminate the thread, void is returned.

The default exit handler quits MzScheme (or MrEd), using its argument as the exit code if it is between 1 and 255 inclusive (meaning ``failure''), or 0 (meaning ``success'') otherwise.

When MzScheme is embedded within another application, the default exit handler may behave differently.

14.3  Input Parsing

MzScheme's input parser obeys the following non-standard rules:

Reading from a custom port can produce arbitrary values generated by the port; see section 11.1.6 for details. If the port generates a non-character value in a position where a character is required (e.g., within a string), the exn:read:non-char exception is raised.

14.4  Output Printing

MzScheme's printer obeys the following non-standard rules:

14.5  Data Sharing in Input and Output

MzScheme can read and print graphs, S-expressions with shared structure (e.g., a cycle). Graphs are described by tagging the shared structure once with #n= (using some decimal integer n with no more than eight digits) and then referencing it later with #n# (using the same number n). For example, the following S-expression describes the infinite list of ones:

#0=(1 . #0#)

If this graph is entered into MzScheme's read-eval-print loop, MzScheme's compiler will loop forever, trying to compile an infinite expression. In contrast, the following expression defines ones to the infinite list of ones, using quote to hide the infinite list from the compiler:

(define ones (quote #0=(1 . #0#)))

A tagged structure can be referenced multiple times. Here, v is defined to be a vector containing the same cons cell in all three slots:

(define v #(#1=(cons 1 2) #1# #1#))

A tag #n= must appear to the left of all references #n#, and all references must appear in the same top-level S-expression as the tag. By default, MzScheme's printer will display a value without showing the shared structure:

#((1 . 2) (1 . 2) (1 . 2))

Graph reading and printing are controlled with the read-accept-graph and print-graph boolean parameters (see section 7.7.1.4). Graph reading is enabled by default, and graph printing is disabled by default. However, when the printer encounters an graph containing a cycle, graph printing is automatically enabled, temporarily. (For this reason, the display, write, and print procedures require memory proportional to the depth of the value being printed.) When graph reading is disabled and a graph is provided as input, the exn:read exception is raised.

If the n in a #n= form or a #n# form contains more than eight digits, the exn:read exception is raised. If a #n# form is not preceded by a #n= form using the same n, the exn:read exception is raised. If two #n= forms are in the same expression for the same n, the exn:read exception is raised.

14.6  Compilation

Normally, compilation happens automatically: when an S-expression is evaluated, it is first compiled and then the compiled code is executed. However, MzScheme can also write and read compiled code. MzScheme can read compiled code much faster than reading S-expression code and compiling it, so compilation can be used to speed up program loading. The MzLib procedure compile-file (see Chapter mz:compilefile in PLT MzLib: Libraries Manual) is sufficient for most compilation purposes.

When a compiled expression is written to an output port, the written form starts with #~. These expressions are essentially assembly code for the MzScheme interpreter, and reading such an expression produces a compiled expression. When a compiled expression contains syntax object constants, the #~ form of the expression drops location information and properties for the syntax objects (see section 12.2 and section 12.6.2).

Never ask MzScheme to evaluate an expression starting with #~ unless compile generated the expression. To keep users from accidentally specifying bad instructions, read will not accept expressions beginning with #~ unless it is specifically enabled through the read-accept-compiled boolean parameter (see section 7.7.1.3). When the default load handler is used to load a file, compiled expression reading is automatically (temporarily) enabled as each expression is read.

A compiled code object may contain uninterned symbols (see section 3.6) that were created by gensym or string->uninterned-symbol. When the compiled object is read via #~, each uninterned symbol in the original expression is mapped to a new uninterned symbol, where multiple instances of a single symbol are consistently mapped to the same new symbol. The original and new symbols have the same printed representation.

Due to the above restrictions, do not use gensym or string->uninterned-symbol to construct an indentifier for a top-level or module binding. Instead, generate distinct identifiers either with generate-temporaries (see section 12.2.2) or by applying the result of a make-syntax-introducer (see section 12.6) to an existing identifier.

14.7  Dynamic Extensions

A dynamically-linked extension library is loaded into MzScheme with (load-extension file-path). The separate document Inside PLT MzScheme contains information about writing MzScheme extensions. An extension can only be loaded once during a MzScheme session, although the extension-writer can provide functionality to handle extra calls to load-extension for a single extension.

As with load, the current load-relative directory (the value of the current-load-relative-directory parameter; see section 7.7.1.6) is set while the extension is loaded. The load-relative-extension procedure is like load-extension, but it loads an extension with a pathname that is relative to the current load-relative directory instead of the current directory.

The load-extension procedure actually just dispatches to the current load extension handler (see section 7.7.1.6). The result of calling load-extension is determined by the extension. If the extension cannot be loaded, the exn:i/o:filesystem exception is raised. The detail field of the exception is 'wrong-version if the load fails because the extension has the wrong version.

14.8  Saving and Restoring Program Images

An image is a memory dump from a running MzScheme program that can be later restored (one or more times) to continue running the program from the point of the dump. Images are only supported for statically-linked Unix versions of MzScheme (and MrEd). There are a few special restrictions on images:

(write-image-to-file file-path [cont-proc]) copies the state of the entire MzScheme process37 to file-path, replacing file-path if it already exists. If images are not supported, the exn:misc:unsupported exception is raised. If cont-proc is #f, then the MzScheme or MrEd process exits immediately after creating the image. Otherwise, cont-proc must be a procedure of no arguments, and the return value(s) of the call to write-image-to-file is (cont-proc). The default value for cont-proc is void.

(read-image-from-file file-path arg-vector) restores the image saved to file-path. Once the image is restored, execution of the original program continues with the return from write-image-to-file; the return value in the restored program is the a vector of strings arg-vector. A successful call to read-image-from-file never returns because the restored program is overlayed over the current program. The vector arg-vector must contain no more than 20 strings, and the total length of the strings must be no more than 2048 characters.

If an error is encountered while reading or writing an image, the exn:i/o:filesystem exception is raised or exn:misc exception is raised. Certain errors during read-image-from-file are unrecoverable; in case of such errors, MzScheme prints an error message and exits immediately.

An image can also be restored by starting the stand-alone version of MzScheme or MrEd with the --restore flag followed by the image filename. The return value from write-image-to-file in the restored program is a vector of strings that are the extra arguments provided on the command line after the image filename (if any).


34 The eval procedure actually calls the current evaluation handler (see section 7.7.1.5) with e to evaluate the expression.

35 The load procedure actually just sets the current load-relative directory and calls the current load handler (see section 7.7.1.6) with file-path to load the file. The description of load here is actually a description of the default load handler.

36 The load/use-compiled procedure actually just calls the current load/use-compiled handler (see section 7.7.1.6). The default handler, in turn, calls the load or load-extension handler, depending on the type of file that is loaded.

37 The set of environment variables is not saved. When an image is restored, the environment variables of the restoring program are transferred into the restored program.