All major Scheme dialects implement the R5RS specification [22]. By using only the features documented in the R5RS, one can write Scheme code that is portable across the dialects. However, the R5RS, either for want of consensus or because of inevitable system dependencies, remains silent on several matters that non-trivial programming cannot ignore. The various dialects have therefore had to solve these matters in a non-standard and idiosyncratic manner.
This book uses the MzScheme [9]
dialect of Scheme, and thereby uses several
features that are nonstandard. The complete
list of the dialect-dependent features used
in this book is: the command-line (both for
opening a listener session and for shell scripts),
define-macro
, delete-file
, file-exists?
,
file-or-directory-modify-seconds
, fluid-let
,
gensym
, getenv
, get-output-string
,
load-relative
, open-input-string
,
open-output-string
, read-line
, reverse!
,
system
, unless
and when
.
All but two of these
are present in the default environment of MzScheme. The missing two,
define-macro
and system
, are provided in
standard MzScheme libraries, which can be explicitly loaded into
MzScheme using the forms:
(require (lib "defmacro.ss")) ;provides define-macro (require (lib "process.ss")) ;provides system
A good place to place these forms is the MzScheme
initialization file, which is the file
.mzschemerc
in the user's home directory.
Some of the nonstandard features (eg, file-exists?
,
delete-file
) are in fact de facto standards and are present
in many Schemes. Some other features (eg, when
,
unless
) have more or less ``plug-in'' definitions
(given in this book) that can be loaded into any Scheme
dialect that doesn't have them primitively. The rest
require a dialect-specific definition
(eg, load-relative
).
This chapter describes how to incorporate into your Scheme dialect the nonstandard features used in this book. For further detail about your Scheme dialect, consult the documentation provided by its implementor (appendix E).
To invoke the Guile [13] listener, type guile
. To invoke
SCM [20], type scm
.
To invoke STk [14], type snow
. This is the
``no window'' executable, which is enough for the purposes
of this book.
Like Mzscheme, many Scheme dialects load,
if available, an
initialization file (or init file), usually
supplied in the user's home directory. The init file
is a convenient location in which to place definitions
for nonstandard features. Eg, the nonstandard
procedure file-or-directory-modify-seconds
can be
added to the Guile dialect of Scheme by putting
the following code in Guile's init file, which
is ~/.guile
:
(define file-or-directory-modify-seconds (lambda (f) (vector-ref (stat f) 9)))
The init files for SCM and STk are
~/ScmInit.scm
and ~/.stkrc
respectively.
The initial line for a shell script written in Guile is:
":";exec guile -s $0 "$@"
In the script, the procedure-call (command-line)
returns
the list of the script's name and arguments. To access
just the arguments, take the cdr
of this list.
A shell script written in SCM starts out as:
":";exec scm -l $0 "$@"
In the script, the variable *argv*
contains
the list of the Scheme executable name, the script's
name, the option -l
, and the script's arguments.
To
access just the arguments, take the cdddr
of this list.
STk shell scripts start out as:
":";exec snow -f $0 "$@"
In the script, the variable *argv*
contains
the list of the script's arguments.
define-macro
The define-macro
used in the text occurs in
the Scheme dialects Bigloo [29], Gambit
[6], Guile, MzScheme and Pocket Scheme
[15]. There are minor variations in
how macros are defined in the other Scheme dialects.
The rest of this section will point out how these
other dialects notate the following code fragment:
(define-macro MACRO-NAME (lambda MACRO-ARGS MACRO-BODY ...))
In MIT Scheme [25], this is written as:
(syntax-table-define system-global-syntax-table 'MACRO-NAME (macro MACRO-ARGS MACRO-BODY ...))
In SCM and Kawa [3]:
(defmacro MACRO-NAME MACRO-ARGS MACRO-BODY ...)
In STk:
(define-macro (MACRO-NAME . MACRO-ARGS) MACRO-BODY ...)
load-relative
The procedure load-relative
may be defined for Guile as follows:
(define load-relative (lambda (f) (let* ((n (string-length f)) (full-pathname? (and (> n 0) (let ((c0 (string-ref f 0))) (or (char=? c0 #\/) (char=? c0 #\~)))))) (basic-load (if full-pathname? f (let ((clp (current-load-port))) (if clp (string-append (dirname (port-filename clp)) "/" f) f)))))))
For SCM:
(define load-relative (lambda (f) (let* ((n (string-length f)) (full-pathname? (and (> n 0) (let ((c0 (string-ref f 0))) (or (char=? c0 #\/) (char=? c0 #\~)))))) (load (if (and *load-pathname* full-pathname?) (in-vicinity (program-vicinity) f) f)))))
For STk, the following definition for load-relative
works only if you discipline yourself to not use load
:
(define *load-pathname* #f) (define stk%load load) (define load-relative (lambda (f) (fluid-let ((*load-pathname* (if (not *load-pathname*) f (let* ((n (string-length f)) (full-pathname? (and (> n 0) (let ((c0 (string-ref f 0))) (or (char=? c0 #\/) (char=? c0 #\~)))))) (if full-pathname? f (string-append (dirname *load-pathname*) "/" f)))))) (stk%load *load-pathname*)))) (define load (lambda (f) (error "Don't use load. Use load-relative instead.")))