#
Appendix A
Scheme dialects

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 (or init file), which, on Unix, is the file `.mzschemerc` in the user's home directory.11

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

## A.1 Invocation and init files

Like MzScheme, many Scheme dialects load, if available, an 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 [13] 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)))

Also, the various Scheme dialects have their own distinctively named commands to invoke their respective listeners. The following table lists the invoking commands and init files for some Scheme dialects:

Dialect name | Command | Init file
Bigloo | `bigloo` | `~/.bigloorc`
Chicken | `csi` | `~/.csirc`
Gambit | `gsi` | `~/gambc.scm`
Guile | `guile` | `~/.guile`
Kawa | `kawa` | `~/.kawarc.scm`
MIT Scheme (Unix) | `scheme` | `~/.scheme.init`
MIT Scheme (Win) | `scheme` | `~/scheme.ini`
MzScheme (Unix, Mac OS X) | `mzscheme` | `~/.mzschemerc`
MzScheme (Win, Mac OS Classic) | `mzscheme` | `~/mzschemerc.ss`
SCM | `scm` | `~/ScmInit.scm`
STk | `snow` | `~/.stkrc`

## A.2 Shell scripts

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.

## A.3 `define-macro`

The `define-macro` used in the text occurs in the Scheme dialects Bigloo [29], Chicken [31], 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] version 7.7.1 and later, this is written as:

    (define-syntax MACRO-NAME
      (rsc-macro-transformer
        (let ((xfmr (lambda MACRO-ARGS MACRO-BODY ...)))
          (lambda (e r)
            (apply xfmr (cdr e))))))

In older versions of MIT Scheme:

    (syntax-table-define system-global-syntax-table 'MACRO-NAME
      (macro MACRO-ARGS
        MACRO-BODY ...))

In SCM [20] and Kawa [3]:

    (defmacro MACRO-NAME MACRO-ARGS
      MACRO-BODY ...)

In STk [14]:

    (define-macro (MACRO-NAME . MACRO-ARGS)
      MACRO-BODY ...)

## A.4 `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.")))

11 We will use `~/filename` to denote the file called `filename` in the user's home directory.