[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


The Language

Standards Compliance

Scm conforms to the [IEEE], IEEE Standard 1178-1990. IEEE Standard for the Scheme Programming Language. and [R5RS], Revised(5) Report on the Algorithmic Language Scheme. All the required features of these specifications are supported. Many of the optional features are supported as well.

Optionals of [R5RS] Supported by SCM

- and / of more than 2 arguments
exp
log
sin
cos
tan
asin
acos
atan
sqrt
expt
make-rectangular
make-polar
real-part
imag-part
magnitude
angle
exact->inexact
inexact->exact
See section `Numerical operations' in Revised(5) Scheme.
with-input-from-file
with-output-to-file
See section `Ports' in Revised(5) Scheme.
load
transcript-on
transcript-off
See section `System interface' in Revised(5) Scheme.

Optionals of [R5RS] not Supported by SCM

numerator
denominator
rationalize
See section `Numerical operations' in Revised(5) Scheme.

[SLIB] Features of SCM and SCMLIT

delay
full-continuation
ieee-p1178
object-hash
rev4-report
source
See SLIB file `Template.scm'.
current-time
See section `Time and Date' in SLIB.
defmacro
See section `Defmacro' in SLIB.
getenv
system
See section `System Interface' in SLIB.
hash
See section `Hashing' in SLIB.
logical
See section `Bit-Twiddling' in SLIB.
multiarg-apply
See section `Multi-argument Apply' in SLIB.
multiarg/and-
See section `Multi-argument / and -' in SLIB.
rev4-optional-procedures
See section `Rev4 Optional Procedures' in SLIB.
string-port
See section `String Ports' in SLIB.
tmpnam
See section `Input/Output' in SLIB.
transcript
See section `Transcripts' in SLIB.
vicinity
See section `Vicinity' in SLIB.
with-file
See section `With-File' in SLIB.

[SLIB] Features of SCM

array
See section `Arrays' in SLIB.
array-for-each
See section `Array Mapping' in SLIB.
bignum
complex
inexact
rational
real
See section `Require' in SLIB.

Storage

Function: vector-set-length! object length
Change the length of string, vector, bit-vector, or uniform-array object to length. If this shortens object then the remaining contents are lost. If it enlarges object then the contents of the extended part are undefined but the original part is unchanged. It is an error to change the length of literal datums. The new object is returned.

Function: copy-tree obj
Function: @copy-tree obj
See section `Tree Operations' in SLIB. This extends the SLIB version by also copying vectors. Use @copy-tree if you depend on this feature; copy-tree could get redefined.

Function: acons obj1 obj2 obj3
Returns (cons (cons obj1 obj2) obj3).
(set! a-list (acons key datum a-list))

Adds a new association to a-list.

Callback procedure: gc-hook ...
Allows a Scheme procedure to be run shortly after each garbage collection. This procedure will not be run recursively. If it runs long enough to cause a garbage collection before returning a warning will be printed.

To remove the gc-hook, (set! gc-hook #f).

Function: add-finalizer object finalizer
object may be any garbage collected object, that is, any object other than an immediate integer, character, or special token such as #f or #t, See section Immediates. finalizer is a thunk, or procedure taking no arguments.

finalizer will be invoked asynchronously exactly once some time after object becomes eligible for garbage collection. A reference to object in the environment of finalizer will not prevent finalization, but will delay the reclamation of object at least until the next garbage collection. A reference to object in some other object's finalizer will necessarily prevent finalization until both objects are eligible for garbage collection.

Finalizers are not run in any predictable order. All finalizers will be run by the time the program ends.

This facility was based on the paper by Simon Peyton Jones, et al, "Stretching the storage manager: weak pointers and stable names in Haskell", Proc. 11th International Workshop on the Implementation of Functional Languages, The Netherlands, September 7-10 1999, Springer-Verlag LNCS.

Time

Constant: internal-time-units-per-second

Is the integer number of internal time units in a second.

Function: get-internal-run-time
Returns the integer run time in internal time units from an unspecified starting time. The difference of two calls to get-internal-run-time divided by internal-time-units-per-second will give elapsed run time in seconds.

Function: get-internal-real-time
Returns the integer time in internal time units from an unspecified starting time. The difference of two calls to get-internal-real-time divided by interal-time-units-per-second will give elapsed real time in seconds.

Function: current-time
Returns the time since 00:00:00 GMT, January 1, 1970, measured in seconds. See section `Time and Date' in SLIB. current-time is used in section `Time and Date' in SLIB.

Interrupts

Function: ticks n
Returns the number of ticks remaining till the next tick interrupt. Ticks are an arbitrary unit of evaluation. Ticks can vary greatly in the amount of time they represent.

If n is 0, any ticks request is canceled. Otherwise a ticks-interrupt will be signaled n from the current time. ticks is supported if SCM is compiled with the ticks flag defined.

Callback procedure: ticks-interrupt ...
Establishes a response for tick interrupts. Another tick interrupt will not occur unless ticks is called again. Program execution will resume if the handler returns. This procedure should (abort) or some other action which does not return if it does not want processing to continue.

Function: alarm secs
Returns the number of seconds remaining till the next alarm interrupt. If secs is 0, any alarm request is canceled. Otherwise an alarm-interrupt will be signaled secs from the current time. ALARM is not supported on all systems.

Function: milli-alarm millisecs interval
Function: virtual-alarm millisecs interval
Function: profile-alarm millisecs interval
milli-alarm is similar to alarm, except that the first argument millisecs, and the return value are measured in milliseconds rather than seconds. If the optional argument interval is supplied then alarm interrupts will be scheduled every interval milliseconds until turned off by a call to milli-alarm or alarm.

virtual-alarm and profile-alarm are similar. virtual-alarm decrements process execution time rather than real time, and causes SIGVTALRM to be signaled. profile-alarm decrements both process execution time and system execution time on behalf of the process, and causes SIGPROF to be signaled.

milli-alarm, virtual-alarm, and profile-alarm are supported only on systems providing the setitimer system call.

Callback procedure: user-interrupt ...
Callback procedure: alarm-interrupt ...
Callback procedure: virtual-alarm-interrupt ...
Callback procedure: profile-alarm-interrupt ...
Establishes a response for SIGINT (control-C interrupt) and SIGALRM, SIGVTALRM, and SIGPROF interrupts. Program execution will resume if the handler returns. This procedure should (abort) or some other action which does not return if it does not want processing to continue after it returns.

Interrupt handlers are disabled during execution system and ed procedures.

To unestablish a response for an interrupt set the handler symbol to #f. For instance, (set! user-interrupt #f).

Callback procedure: out-of-storage ...
Callback procedure: could-not-open ...
Callback procedure: end-of-program ...
Callback procedure: hang-up ...
Callback procedure: arithmetic-error ...
Establishes a response for storage allocation error, file opening error, end of program, SIGHUP (hang up interrupt) and arithmetic errors respectively. This procedure should (abort) or some other action which does not return if it does not want the default error message to also be displayed. If no procedure is defined for hang-up then end-of-program (if defined) will be called.

To unestablish a response for an error set the handler symbol to #f. For instance, (set! could-not-open #f).

Process Synchronization

An exchanger is a procedure of one argument regulating mutually exclusive access to a resource. When a exchanger is called, its current content is returned, while being replaced by its argument in an atomic operation.

Function: make-exchanger obj

Returns a new exchanger with the argument obj as its initial content.

(define queue (make-exchanger (list a)))

A queue implemented as an exchanger holding a list can be protected from reentrant execution thus:

(define (pop queue)
  (let ((lst #f))
    (dynamic-wind
        (lambda () (set! lst (queue #f)))
        (lambda () (and lst (not (null? lst))
                        (let ((ret (car lst)))
                          (set! lst (cdr lst))
                          ret)))
        (lambda () (and lst (queue lst))))))

(pop queue)         => a

(pop queue)         => #f

Function: make-arbiter name

Returns an object of type arbiter and name name. Its state is initially unlocked.

Function: try-arbiter arbiter

Returns #t and locks arbiter if arbiter was unlocked. Otherwise, returns #f.

Function: release-arbiter arbiter

Returns #t and unlocks arbiter if arbiter was locked. Otherwise, returns #f.

Files and Ports

These procedures generalize and extend the standard capabilities in section `Ports' in Revised(5) Scheme.

Opening and Closing

Function: open-file string modes
Function: try-open-file string modes
Returns a port capable of receiving or delivering characters as specified by the modes string. If a file cannot be opened #f is returned.

Internal functions opening files callback to the SCM function open-file. You can extend open-file by redefining it. try-open-file is the primitive procedure; Do not redefine try-open-file!

Constant: open_read
Constant: open_write
Constant: open_both
Contain modes strings specifying that a file is to be opened for reading, writing, and both reading and writing respectively.

Both input and output functions can be used with io-ports. An end of file must be read or a two-argument file-position done on the port between a read operation and a write operation or vice-versa.

Function: _ionbf modestr
Returns a version of modestr which when open-file is called with it as the second argument will return an unbuffered port. An input-port must be unbuffered in order for char-ready? and wait-for-input to work correctly on it. The initial value of (current-input-port) is unbuffered if the platform supports it.

Function: _tracked modestr
Returns a version of modestr which when open-file is called with it as the second argument will return a tracked port. A tracked port maintains current line and column numbers, which may be queried with port-line and port-column.

Function: _exclusive modestr
Returns a version of modestr which when open-file is called with it as the second argument will return a port only if the named file does not already exist. This functionality is provided by calling try-create-file See section I/O-Extensions, which is not available for all platforms.

Function: open-ports
Returns a list of all currently open ports, excluding string ports, see See section `String Ports' in SLIB. This may be useful after a fork See section Posix Extensions, or for debugging. Bear in mind that ports that would be closed by gc will be kept open by a reference to this list.

Function: close-port port
Closes port. The same as close-input-port and close-output-port.

Port Properties

Function: port-closed? port
Returns #t if port is closed.

Function: port-type obj
If obj is not a port returns false, otherwise returns a symbol describing the port type, for example string or pipe.

Function: port-filename port
Returns the filename port was opened with. If port is not open to a file the result is unspecified.

Function: file-position port
Function: file-position port #f
Returns the current position of the character in port which will next be read or written. If port is open to a non-file then #f is returned.

Function: file-position port k
Sets the current position in port which will next be read or written. If successful, #f is returned. If port is open to a non-file, then file-position returns #f.

Function: port-line port
Function: port-column port
If port is a tracked port, return the current line (column) number, otherwise return #f. Line and column numbers begin with 1. The column number applies to the next character to be read; if that character is a newline, then the column number will be one more than the length of the line.

Function: freshline port
Outputs a newline to optional argument port unless the current output column number of port is known to be zero, ie output will start at the beginning of a new line. port defaults to current-output-port. If port is not a tracked port freshline is equivalent to newline.

Function: isatty? port
Returns #t if port is input or output to a serial non-file device.

procedure: char-ready?
procedure: char-ready? port

Returns #t if a character is ready on the input port and returns #f otherwise. If char-ready? returns #t then the next read-char operation on the given port is guaranteed not to hang. If the port is at end of file then char-ready? returns #t. Port may be omitted, in which case it defaults to the value returned by current-input-port.

Rationale Char-ready? exists to make it possible for a program to accept characters from interactive ports without getting stuck waiting for input. Any input editors associated with such ports must ensure that characters whose existence has been asserted by char-ready? cannot be rubbed out. If char-ready? were to return #f at end of file, a port at end of file would be indistinguishable from an interactive port that has no ready characters.

procedure: wait-for-input x
procedure: wait-for-input x port1 ...
Returns a list those ports port1 ... which are char-ready?. If none of port1 ... become char-ready? within the time interval of x seconds, then #f is returned. The port1 ... arguments may be omitted, in which case they default to the list of the value returned by current-input-port.

Port Redirection

Function: current-error-port
Returns the current port to which diagnostic output is directed.

Function: with-error-to-file string thunk
thunk must be a procedure of no arguments, and string must be a string naming a file. The file is opened for output, an output port connected to it is made the default value returned by current-error-port, and the thunk is called with no arguments. When the thunk returns, the port is closed and the previous default is restored. With-error-to-file returns the value yielded by thunk.

Function: with-input-from-port port thunk
Function: with-output-to-port port thunk
Function: with-error-to-port port thunk
These routines differ from with-input-from-file, with-output-to-file, and with-error-to-file in that the first argument is a port, rather than a string naming a file.

Function: call-with-outputs thunk proc
Calls the thunk procedure while the current-output-port and current-error-port are directed to string-ports. If thunk returns, the proc procedure is called with the output-string, the error-string, and the value returned by thunk. If thunk does not return a value (perhaps because of error), proc is called with just the output-string and the error-string as arguments.

Soft Ports

A soft-port is a port based on a vector of procedures capable of accepting or delivering characters. It allows emulation of I/O ports.

Function: make-soft-port vector modes
Returns a port capable of receiving or delivering characters as specified by the modes string (see section Files and Ports). vector must be a vector of length 5. Its components are as follows:
  1. procedure accepting one character for output
  2. procedure accepting a string for output
  3. thunk for flushing output
  4. thunk for getting one character
  5. thunk for closing port (not by garbage collection)

For an output-only port only elements 0, 1, 2, and 4 need be procedures. For an input-only port only elements 3 and 4 need be procedures. Thunks 2 and 4 can instead be #f if there is no useful operation for them to perform.

If thunk 3 returns #f or an eof-object (see section `Input' in Revised(5) Scheme) it indicates that the port has reached end-of-file. For example:

If it is necessary to explicitly close the port when it is garbage collected, (see section Interrupts).

(define stdout (current-output-port))
(define p (make-soft-port
           (vector
            (lambda (c) (write c stdout))
            (lambda (s) (display s stdout))
            (lambda () (display "." stdout))
            (lambda () (char-upcase (read-char)))
            (lambda () (display "@" stdout)))
           "rw"))

(write p p) => #<input-output-soft#\space45d10#\>

Eval and Load

Function: try-load filename
If the string filename names an existing file, the try-load procedure reads Scheme source code expressions and definitions from the file and evaluates them sequentially and returns #t. If not, try-load returns #f. The try-load procedure does not affect the values returned by current-input-port and current-output-port.

Variable: *load-pathname*
Is set to the pathname given as argument to load, try-load, and dyn:link (see section `Compiling And Linking' in Hobbit). *load-pathname* is used to compute the value of section `Vicinity' in SLIB.

Function: eval obj
Alias for section `System' in SLIB.

Function: eval-string str
Returns the result of reading an expression from str and evaluating it. eval-string does not change *load-pathname* or line-number.

Function: load-string str
Reads and evaluates all the expressions from str. As with load, the value returned is unspecified. load-string does not change *load-pathname* or line-number.

Function: line-number
Returns the current line number of the file currently being loaded.

Line Numbers

Scheme code defined by load may optionally contain line number information. Currently this information is used only for reporting expansion time errors, but in the future run-time error messages may also include line number information.

Function: try-load pathname reader
This is the primitive for loading, pathname is the name of a file containing Scheme code, and optional argument reader is a function of one argument, a port. reader should read and return Scheme code as list structure. The default value is read, which is used if reader is not supplied or is false.

Line number objects are disjoint from integers or other Scheme types. When evaluated or loaded as Scheme code, an s-expression containing a line-number in the car is equivalent to the cdr of the s-expression. A pair consisting of a line-number in the car and a vector in the cdr is equivalent to the vector. The meaning of s-expressions with line-numbers in other positions is undefined.

Function: read-numbered port
Behaves like read, except that

Function: integer->line-number int
Returns a line-number object with value int. int should be an exact non-negative integer.

Function: line-number->integer linum
Returns the value of line-number object linum as an integer.

Function: line-number? obj
Returns true if and only if obj is a line-number object.

Function: read-for-load port
Behaves like read, except that load syntaxes are enabled.

Variable: *load-reader*
Variable: *slib-load-reader*
The value of *load-reader* should be a value acceptable as the second argument to try-load (note that #f is acceptable). This value will be used to read code during calls to scm:load. The value of *slib-load-reader* will similarly be used during calls to slib:load and require.

In order to disable all line-numbering, it is sufficient to set! *load-reader* and *slib-load-reader* to #f.

Lexical Conventions

Common-Lisp Read Syntax

Read syntax: #\token
If token is a sequence of two or more digits, then this syntax is equivalent to #.(integer->char (string->number token 8)).

If token is C-, c-, or ^ followed by a character, then this syntax is read as a control character. If token is M- or m- followed by a character, then a meta character is read. c- and m- prefixes may be combined.

Read syntax: #+ feature form
If feature is provided? then form is read as a scheme expression. If not, then form is treated as whitespace.

Feature is a boolean expression composed of symbols and and, or, and not of boolean expressions.

For more information on provided?, See section `Require' in SLIB.

Read syntax: #- feature form
is equivalent to #+(not feature) expression.

Read syntax: #| any thing |#
Is a balanced comment. Everything up to the matching |# is ignored by the read. Nested #|...|# can occur inside any thing.

Load sytax is Read syntax enabled for read only when that read is part of loading a file or string. This distinction was made so that reading from a datafile would not be able to corrupt a scheme program using `#.'.

Load syntax: #. expression
Is read as the object resulting from the evaluation of expression. This substitution occurs even inside quoted structure.

In order to allow compiled code to work with #. it is good practice to define those symbols used inside of expression with #.(define ...). For example:

#.(define foo 9)                        => #<unspecified>
'(#.foo #.(+ foo foo))                  => (9 18)

Load syntax: #' form
is equivalent to form (for compatibility with common-lisp).

Load Syntax

#! is the unix mechanism for executing scripts. See section Unix Scheme Scripts for the full description of how this comment supports scripting.

Load syntax: #?line
Load syntax: #?column
Return integers for the current line and column being read during a load.

Load syntax: #?file
Returns the string naming the file currently being loaded. This path is the string passed to load, possibly with `.scm' appended.

Documentation and Comments

procedure: procedure-documentation proc
Returns the documentation string of proc if it exists, or #f if not.

If the body of a lambda (or the definition of a procedure) has more than one expression, and the first expression (preceeding any internal definitions) is a string, then that string is the documentation string of that procedure.

(procedure-documentation (lambda (x) "Identity" x)) => "Identity"
(define (square x)
    "Return the square of X."
    (* x x))
=> #<unspecified>
(procedure-documentation square) => "Return the square of X."

Function: comment string1 ...
Appends string1 ... to the strings given as arguments to previous calls comment.

Function: comment
Returns the (appended) strings given as arguments to previous calls comment and empties the current string collection.

Load syntax: #;text-till-end-of-line
Behaves as (comment "text-till-end-of-line").

Modifying Read Syntax

Callback procedure: read:sharp c port
If a # followed by a character (for a non-standard syntax) is encountered by read, read will call the value of the symbol read:sharp with arguments the character and the port being read from. The value returned by this function will be the value of read for this expression unless the function returns #<unspecified> in which case the expression will be treated as whitespace. #<unspecified> is the value returned by the expression (if #f #f).

Callback procedure: load:sharp c port
Dispatches like read:sharp, but only during loads. The read-syntaxes handled by load:sharp are a superset of those handled by read:sharp. load:sharp calls read:sharp if none of its syntaxes match c.

Callback procedure: char:sharp token
If the sequence #\ followed by a non-standard character name is encountered by read, read will call the value of the symbol char:sharp with the token (a string of length at least two) as argument. If the value returned is a character, then that will be the value of read for this expression, otherwise an error will be signaled.

Note When adding new # syntaxes, have your code save the previous value of load:sharp, read:sharp, or char:sharp when defining it. Call this saved value if an invocation's syntax is not recognized. This will allow #+, #-, and section Uniform Arrays to still be supported (as they dispatch from read:sharp).

Syntax

SCM provides a native implementation of defmacro. See section `Defmacro' in SLIB.

When built with `-F macro' build option (see section Build Options) and `*syntax-rules*' is non-false, SCM also supports [R5RS] syntax-rules macros. See section `Macros' in Revised(5) Scheme.

Other Scheme Syntax Extension Packages from SLIB can be employed through the use of `macro:eval' and `macro:load'; Or by using the SLIB read-eval-print-loop:

(require 'repl)
(repl:top-level macro:eval)

With the appropriate catalog entries (see section `Library Catalogs' in SLIB), files using macro packages will automatically use the correct macro loader when `require'd.

Define and Set

Special Form: defined? symbol
Equivalent to #t if symbol is a syntactic keyword (such as if) or a symbol with a value in the top level environment (see section `Variables and regions' in Revised(5) Scheme). Otherwise equivalent to #f.

Special Form: defvar identifier initial-value
If identifier is unbound in the top level environment, then identifier is defined to the result of evaluating the form initial-value as if the defvar form were instead the form (define identifier initial-value) . If identifier already has a value, then initial-value is not evaluated and identifier's value is not changed. defvar is valid only when used at top-level.

Special Form: defconst identifier value
If identifier is unbound in the top level environment, then identifier is defined to the result of evaluating the form value as if the defconst form were instead the form (define identifier value) . If identifier already has a value, then value is not evaluated, identifier's value is not changed, and an error is signaled. defconst is valid only when used at top-level.

Special Form: set! (variable1 variable2 ...) <expression>

The identifiers variable1, variable2, ... must be bound either in some region enclosing the `set!' expression or at top level.

<Expression> is evaluated, and the elements of the resulting list are stored in the locations to which each corresponding variable is bound. The result of the `set!' expression is unspecified.

(define x 2)
(define y 3)
(+ x y)                              => 5
(set! (x y) (list 4 5))              => unspecified
(+ x y)                              => 9

Special Form: qase key clause1 clause2 ...
qase is an extension of standard Scheme case: Each clause of a qase statement must have as first element a list containing elements which are:

A qase statement is equivalent to a case statement in which these symbolic constants preceded by commas have been replaced by the values of the constants, and all symbolic constants preceded by comma-at-signs have been replaced by the elements of the list values of the constants. This use of comma, (or, equivalently, unquote) is similar to that of quasiquote except that the unquoted expressions must be symbolic constants.

Symbolic constants are defined using defconst, their values are substituted in the head of each qase clause during macro expansion. defconst constants should be defined before use. qase can be substituted for any correct use of case.

(defconst unit '1)
(defconst semivowels '(w y))
(qase (* 2 3)
  ((2 3 5 7) 'prime)
  ((,unit 4 6 8 9) 'composite))        ==>  composite
(qase (car '(c d))
  ((a) 'a)
  ((b) 'b))                            ==>  unspecified
(qase (car '(c d))
  ((a e i o u) 'vowel)
  ((,@semivowels) 'semivowel)
  (else 'consonant))                   ==>  consonant

Defmacro

SCM supports the following constructs from Common Lisp: defmacro, macroexpand, macroexpand-1, and gentemp. See section `Defmacro' in SLIB.

SCM defmacro is extended over that described for SLIB:

(defmacro (macro-name . arguments) body)

is equivalent to

(defmacro macro-name arguments body)

As in Common Lisp, an element of the formal argument list for defmacro may be a possibly nested list, in which case the corresponding actual argument must be a list with as many members as the formal argument. Rest arguments are indicated by improper lists, as in Scheme. It is an error if the actual argument list does not have the tree structure required by the formal argument list.

For example:

(defmacro (let1 ((name value)) . body)
    `((lambda (,name) ,@body) ,value))

(let1 ((x (foo))) (print x) x) == ((lambda (x) (print x) x) (foo))

(let1 not legal syntax) error--> not "does not match" ((name value))

Syntax-Rules

SCM supports [R5RS] syntax-rules macros See section `Macros' in Revised(5) Scheme.

The pattern language is extended by the syntax (... <obj>), which is identical to <obj> except that ellipses in <obj> are treated as ordinary identifiers in a template, or as literals in a pattern. In particular, (... ...) quotes the ellipsis token ... in a pattern or template.

For example:

(define-syntax check-tree
  (syntax-rules ()
    ((_ (?pattern (... ...)) ?obj)
     (let loop ((obj ?obj))
       (or (null? obj)
           (and (pair? obj)
                (check-tree ?pattern (car obj))
                (loop (cdr obj))))))
    ((_ (?first . ?rest) ?obj)
     (let ((obj ?obj))
       (and (pair? obj)
            (check-tree ?first (car obj))
            (check-tree ?rest (cdr obj)))))
    ((_ ?atom ?obj) #t)))

(check-tree ((a b) ...) '((1 2) (3 4) (5 6))) => #t

(check-tree ((a b) ...) '((1 2) (3 4) not-a-2list) => #f

Note that although the ellipsis is matched as a literal token in the defined macro it is not included in the literals list for syntax-rules.

The pattern language is also extended to support identifier macros. A reference to an identifier macro keyword that is not the first identifier in a form may expand into Scheme code, rather than raising a "keyword as variable" error. The pattern for expansion of such a bare macro keyword is a single identifier, as in other syntax rules the identifier is ignored.

For example:

(define-syntax eight
    (syntax-rules ()
      (_ 8)))

(+ 3 eight) => 11
(eight) => ERROR
(set! eight 9) => ERROR

Macro Primitives

Function: procedure->syntax proc
Returns a macro which, when a symbol defined to this value appears as the first symbol in an expression, returns the result of applying proc to the expression and the environment.

Function: procedure->macro proc
Function: procedure->memoizing-macro proc
Function: procedure->identifier-macro
Returns a macro which, when a symbol defined to this value appears as the first symbol in an expression, evaluates the result of applying proc to the expression and the environment. The value returned from proc which has been passed to PROCEDURE->MEMOIZING-MACRO replaces the form passed to proc. For example:
(defsyntax trace
  (procedure->macro
   (lambda (x env) `(set! ,(cadr x) (tracef ,(cadr x) ',(cadr x))))))

(trace foo) == (set! foo (tracef foo 'foo)).

PROCEDURE->IDENTIFIER-MACRO is similar to PROCEDURE->MEMOIZING-MACRO except that proc is also called in case the symbol bound to the macro appears in an expression but not as the first symbol, that is, when it looks like a variable reference. In that case, the form passed to proc is a single identifier.

Special Form: defsyntax name expr
Defines name as a macro keyword bound to the result of evaluating expr, which should be a macro. Using define for this purpose may not result in name being interpreted as a macro keyword.

Environment Frames

An environment is a list of frames representing lexical bindings. Only the names and scope of the bindings are included in environments passed to macro expanders -- run-time values are not included.

There are several types of environment frames:

((lambda (variable1 ...) ...) value1 ...)
(let ((variable1 value1) (variable2 value2) ...) ...)
(letrec ((variable1 value1) ...) ...)
result in a single enviroment frame:

(variable1 variable2 ...)

(let ((variable1 value1)) ...)
(let* ((variable1 value1) ...) ...)
result in an environment frame for each variable:

variable1 variable2 ...

(let-syntax ((key1 macro1) (key2 macro2)) ...)
(letrec-syntax ((key1 value1) (key2 value2)) ...)
Lexically bound macros result in environment frames consisting of a marker and an alist of keywords and macro objects:

(<env-syntax-marker> (key1 . value1) (key2 . value2))

Currently <env-syntax-marker> is the integer 6.
line numbers
Line numbers (see section Line Numbers) may be included in the environment as frame entries to indicate the line number on which a function is defined. They are ignored for variable lookup.

#<line 8> 

miscellaneous
Debugging information is stored in environments in a plist format: Any exact integer stored as an environment frame may be followed by any value. The two frame entries are ignored when doing variable lookup. Load file names, procedure names, and closure documentation strings are stored in this format.

<env-filename-marker> "foo.scm" <env-procedure-name-marker> foo ...

Currently <env-filename-marker> is the integer 1 and <env-procedure-name-marker> the integer 2.

Special Form: @apply procedure argument-list
Returns the result of applying procedure to argument-list. @apply differs from apply when the identifiers bound by the closure being applied are set!; setting affects argument-list.
(define lst (list 'a 'b 'c))
(@apply (lambda (v1 v2 v3) (set! v1 (cons v2 v3))) lst)
lst           => ((b . c) b c)

Thus a mutable environment can be treated as both a list and local bindings.

Syntactic Hooks for Hygienic Macros

SCM provides a synthetic identifier type for efficient implementation of hygienic macros (for example, syntax-rules see section `Macros' in Revised(5) Scheme) A synthetic identifier may be inserted in Scheme code by a macro expander in any context where a symbol would normally be used. Collectively, symbols and synthetic identifiers are identifiers.

Function: identifier? obj
Returns #t if obj is a symbol or a synthetic identifier, and #f otherwise.

If it is necessary to distinguish between symbols and synthetic identifiers, use the predicate symbol?.

A synthetic identifier includes two data: a parent, which is an identifier, and an environment, which is either #f or a lexical environment which has been passed to a macro expander (a procedure passed as an argument to procedure->macro, procedure->memoizing-macro, or procedure->syntax).

Function: renamed-identifier parent env
Returns a synthetic identifier. parent must be an identifier, and env must either be #f or a lexical environment passed to a macro expander. renamed-identifier returns a distinct object for each call, even if passed identical arguments.

There is no direct way to access all of the data internal to a synthetic identifier, those data are used during variable lookup. If a synthetic identifier is inserted as quoted data then during macro expansion it will be repeatedly replaced by its parent, until a symbol is obtained.

Function: identifier->symbol id
Returns the symbol obtained by recursively extracting the parent of id, which must be an identifier.

Use of Synthetic Identifiers

renamed-identifier may be used as a replacement for gentemp:

(define gentemp
  (let ((name (string->symbol "An unlikely variable")))
    (lambda ()
      (renamed-identifier name #f))))

If an identifier returned by this version of gentemp is inserted in a binding position as the name of a variable then it is guaranteed that no other identifier (except one produced by passing the first to renamed-identifier) may denote that variable. If an identifier returned by gentemp is inserted free, then it will denote the top-level value bound to its parent, the symbol named "An unlikely variable". This behavior, of course, is meant to be put to good use:

(define top-level-foo
  (procedure->memoizing-macro
   (lambda (exp env)
     (renamed-identifier 'foo #f))))

Defines a macro which may always be used to refer to the top-level binding of foo.

(define foo 'top-level)
(let ((foo 'local))
  (top-level-foo))  => top-level

In other words, we can avoid capturing foo.

If a lexical environment is passed as the second argument to renamed-identifier then if the identifier is inserted free its parent will be looked up in that environment, rather than in the top-level environment. The use of such an identifier must be restricted to the lexical scope of its environment.

There is another restriction imposed for implementation convenience: Macros passing their lexical environments to renamed-identifier may be lexically bound only by the special forms let-syntax or letrec-syntax. No error is signaled if this restriction is not met, but synthetic identifier lookup will not work properly.

In order to maintain referential transparency it is necessary to determine whether two identifiers have the same denotation. With synthetic identifiers it is not necessary that two identifiers be eq? in order to denote the same binding.

Function: identifier-equal? id1 id2 env
Returns #t if identifiers id1 and id2 denote the same binding in lexical environment env, and #f otherwise. env must either be a lexical environment passed to a macro transformer during macro expansion or the empty list.

For example,

(define top-level-foo?
  (procedure->memoizing-macro
   (let ((foo-name (renamed-identifier 'foo #f)))
     (lambda (exp env)
       (identifier-equal? (cadr exp) foo-name env)))))

(top-level-foo? foo)  => #t

(let ((foo 'local))
  (top-level-foo? foo))  => #f

Function: @macroexpand1 expr env
If the car of expr denotes a macro in env, then if that macro is a primitive, expr will be returned, if the macro was defined in Scheme, then a macro expansion will be returned. If the car of expr does not denote a macro, the #f is returned.

Function: extended-environment names values env
Returns a new environment object, equivalent to env, which must either be an environment object or null, extended by one frame. names must be an identifier, or an improper list of identifiers, usable as a formals list in a lambda expression. values must be a list of objects long enough to provide a binding for each of the identifiers in names. If names is an identifier or an improper list then vals may be, respectively, any object or an improper list of objects.

Special Form: syntax-quote obj
Synthetic identifiers are converted to their parent symbols by quote and quasiquote so that literal data in macro definitions will be properly transcribed. syntax-quote behaves like quote, but preserves synthetic identifier intact.

Special Form: the-macro mac
the-macro is the simplest of all possible macro transformers: mac may be a syntactic keyword (macro name) or an expression evaluating to a macro, otherwise an error is signaled. mac is evaluated and returned once only, after which the same memoizied value is returned.

the-macro may be used to protect local copies of macros against redefinition, for example:

(@let-syntax ((let (the-macro let)))
   ;; code that will continue to work even if LET is redefined.
        ...)

Special Form: renaming-transformer proc
A low-level "explicit renaming" macro facility very similar to that proposed by W. Clinger [Exrename] is supported. Syntax may be defined in define-syntax, let-syntax, and letrec-syntax using renaming-transformer instead of syntax-rules. proc should evaluate to a procedure accepting three arguments: expr, rename, and compare. expr is a representation of Scheme code to be expanded, as list structure. rename is a procedure accepting an identifier and returning an identifier renamed in the definition environment of the new syntax. compare accepts two identifiers and returns true if and only if both denote the same binding in the usage environment of the new syntax.


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]