SLIB is a portable library for the programming language
Scheme. It provides a platform independent framework for using
packages of Scheme procedures and syntax. As distributed, SLIB
contains useful packages for all Scheme implementations. Its catalog
can be transparently extended to accomodate packages specific to a site,
implementation, user, or directory.
SLIB denotes features by symbols. SLIB maintains a list of
features supported by the Scheme session. The set of features
provided by a session may change over time. Some features are
properties of the Scheme implementation being used. The following
features detail what sort of numbers are available from an
implementation.
'inexact
'rational
'real
'complex
'bignum
Other features correspond to the presence of sets of Scheme procedures
or syntax (macros).
Function:provided?feature
Returns #t if feature is supported by the current Scheme
session.
Procedure:providefeature
Informs SLIB that feature is supported. Henceforth
(provided? feature) will return #t.
SLIB creates and maintains a catalog mapping features to locations
of files introducing procedures and syntax denoted by those features.
At the beginning of each section of this manual, there is a line like
(require 'feature).
The Scheme files comprising SLIB are cataloged so that these feature
names map to the corresponding files.
SLIB provides a form, require, which loads the files providing
the requested feature.
Procedure:requirefeature
If (provided? feature) is true,
then require just returns an unspecified value.
Otherwise, if feature is found in the catalog, then the
corresponding files will be loaded and an unspecified value returned.
Subsequently (provided? feature) will return #t.
Otherwise (feature not found in the catalog), an error is
signaled.
The catalog can also be queried using require:feature->path.
Function:require:feature->pathfeature
If feature is already provided, then returns #t.
Otherwise, if feature is in the catalog, the path or list of paths
associated with feature is returned.
At the start of a session no catalog is present, but is created with the
first catalog inquiry (such as (require 'random)). Several
sources of catalog information are combined to produce the catalog:
standard SLIB packages.
additional packages of interest to this site.
packages specifically for the variety of Scheme which this
session is running.
packages this user wants to always have available. This catalog is the
file `homecat' in the user's HOME directory.
packages germane to working in this (current working) directory. This
catalog is the file `usercat' in the directory to which it applies.
One would typically cd to this directory before starting the
Scheme session.
Catalog files consist of one or more association lists.
In the circumstance where a feature symbol appears in more than one
list, the latter list's association is retrieved. Here are the
supported formats for elements of catalog lists:
(feature . <symbol>)
Redirects to the feature named <symbol>.
(feature . "")
Loads file <path>.
(feature source "")
slib:loads the Scheme source file <path>.
(feature compiled "" ...)
slib:load-compileds the files <path> ....
The various macro styles first require the named macro package,
then just load <path> or load-and-macro-expand <path> as
appropriate for the implementation.
(feature defmacro "")
defmacro:loads the Scheme source file <path>.
(feature macro-by-example "")
defmacro:loads the Scheme source file <path>.
(feature macro "")
macro:loads the Scheme source file <path>.
(feature macros-that-work "")
macro:loads the Scheme source file <path>.
(feature syntax-case "")
macro:loads the Scheme source file <path>.
(feature syntactic-closures "")
macro:loads the Scheme source file <path>.
Here is an example of a `usercat' catalog. A Program in this
directory can invoke the `run' feature with (require 'run).
SLIB combines the catalog information which doesn't vary per user into
the file `slibcat' in the implementation-vicinity. Therefore
`slibcat' needs change only when new software is installed or
compiled. Because the actual pathnames of files can differ from
installation to installation, SLIB builds a separate catalog for each
implementation it is used with.
The definition of *SLIB-VERSION* in SLIB file `require.scm'
is checked against the catalog association of *SLIB-VERSION* to
ascertain when versions have changed. I recommend that the definition
of *SLIB-VERSION* be changed whenever the library is changed. If
multiple implementations of Scheme use SLIB, remember that recompiling
one `slibcat' will fix only that implementation's catalog.
The compilation scripts of Scheme implementations which work with SLIB
can automatically trigger catalog compilation by deleting
`slibcat' or by invoking a special form of require:
Procedure:require'new-catalog
This will load `mklibcat', which compiles and writes a new
`slibcat'.
Another special form of require erases SLIB's catalog, forcing it
to be reloaded the next time the catalog is queried.
Procedure:require#f
Removes SLIB's catalog information. This should be done before saving
an executable image so that, when restored, its catalog will be loaded
afresh.
Each file in the table below is descibed in terms of its
file-system independent vicinity (see section 1.5.2 Vicinity). The entries
of a catalog in the table override those of catalogs above it in the
table.
implementation-vicinity `slibcat'
This file contains the associations for the packages comprising SLIB,
the `implcat' and the `sitecat's. The associations in the
other catalogs override those of the standard catalog.
library-vicinity `mklibcat.scm'
creates `slibcat'.
library-vicinity `sitecat'
This file contains the associations specific to an SLIB installation.
implementation-vicinity `implcat'
This file contains the associations specific to an implementation of
Scheme. Different implementations of Scheme should have different
implementation-vicinity.
implementation-vicinity `mkimpcat.scm'
if present, creates `implcat'.
implementation-vicinity `sitecat'
This file contains the associations specific to a Scheme implementation
installation.
home-vicinity `homecat'
This file contains the associations specific to an SLIB user.
user-vicinity `usercat'
This file contains associations effecting only those sessions whose
working directory is user-vicinity.
Is a list of symbols denoting features supported in this implementation.
*features* can grow as modules are required.
*features* must be defined by all implementations
(see section 7.2 Porting).
Here are features which SLIB (`require.scm') adds to
*features* when appropriate.
'inexact
'rational
'real
'complex
'bignum
For each item, (provided? 'feature) will return #t
if that feature is available, and #f if not.
Variable:*modules*
Is a list of pathnames denoting files which have been loaded.
Variable:*catalog*
Is an association list of features (symbols) and pathnames which will
supply those features. The pathname can be either a string or a pair.
If pathname is a pair then the first element should be a macro feature
symbol, source, or compiled. The cdr of the pathname
should be either a string or a list.
In the following functions if the argument feature is not a symbol
it is assumed to be a pathname.
Function:provided?feature
Returns #t if feature is a member of *features* or
*modules* or if feature is supported by a file already
loaded and #f otherwise.
Procedure:requirefeature
feature is a symbol. If (provided? feature) is true
require returns. Otherwise, if (assq feature
*catalog*) is not #f, the associated files will be loaded and
(provided? feature) will henceforth return #t. An
unspecified value is returned. If feature is not found in
*catalog*, then an error is signaled.
Procedure:requirepathname
pathname is a string. If pathname has not already been
given as an argument to require, pathname is loaded. An
unspecified value is returned.
Procedure:providefeature
Assures that feature is contained in *features* if
feature is a symbol and *modules* otherwise.
Function:require:feature->pathfeature
Returns #t if feature is a member of *features* or
*modules* or if feature is supported by a file already
loaded. Returns a path if one was found in *catalog* under the
feature name, and #f otherwise. The path can either be a string
suitable as an argument to load or a pair as described above for
*catalog*.
A vicinity is a descriptor for a place in the file system. Vicinities
hide from the programmer the concepts of host, volume, directory, and
version. Vicinities express only the concept of a file environment
where a file name can be resolved to a file in a system independent
manner. Vicinities can even be used on flat file systems (which
have no directory structure) by having the vicinity express constraints
on the file name. On most systems a vicinity would be a string. All of
these procedures are file system dependent.
These procedures are provided by all implementations.
Function:make-vicinitypath
Returns the vicinity of path for use by in-vicinity.
Function:program-vicinity
Returns the vicinity of the currently loading Scheme code. For an
interpreter this would be the directory containing source code. For a
compiled system (with multiple files) this would be the directory where
the object or executable files are. If no file is currently loading it
the result is undefined. Warning:program-vicinity can
return incorrect values if your program escapes back into a
load.
Function:library-vicinity
Returns the vicinity of the shared Scheme library.
Function:implementation-vicinity
Returns the vicinity of the underlying Scheme implementation. This
vicinity will likely contain startup code and messages and a compiler.
Function:user-vicinity
Returns the vicinity of the current directory of the user. On most
systems this is `""' (the empty string).
Function:home-vicinity
Returns the vicinity of the user's HOME directory, the directory
which typically contains files which customize a computer environment
for a user. If scheme is running without a user (eg. a daemon) or if
this concept is meaningless for the platform, then home-vicinity
returns #f.
Function:in-vicinityvicinity filename
Returns a filename suitable for use by slib:load,
slib:load-source, slib:load-compiled,
open-input-file, open-output-file, etc. The returned
filename is filename in vicinity. in-vicinity should
allow filename to override vicinity when filename is
an absolute pathname and vicinity is equal to the value of
(user-vicinity). The behavior of in-vicinity when
filename is absolute and vicinity is not equal to the value
of (user-vicinity) is unspecified. For most systems
in-vicinity can be string-append.
Function:sub-vicinityvicinity name
Returns the vicinity of vicinity restricted to name. This
is used for large systems where names of files in subsystems could
conflict. On systems with directory structure sub-vicinity will
return a pathname of the subdirectory name of
vicinity.
These constants and procedures describe characteristics of the Scheme
and underlying operating system. They are provided by all
implementations.
Constant:char-code-limit
An integer 1 larger that the largest value which can be returned by
char->integer.
Constant:most-positive-fixnum
In implementations which support integers of practically unlimited size,
most-positive-fixnum is a large exact integer within the range of
exact integers that may result from computing the length of a list,
vector, or string.
In implementations which do not support integers of practically
unlimited size, most-positive-fixnum is the largest exact integer
that may result from computing the length of a list, vector, or string.
Constant:slib:tab
The tab character.
Constant:slib:form-feed
The form-feed character.
Function:software-type
Returns a symbol denoting the generic operating system type. For
instance, unix, vms, macos, amiga, or
ms-dos.
Function:slib:report-version
Displays the versions of SLIB and the underlying Scheme implementation
and the name of the operating system. An unspecified value is returned.
(slib:report-version) => slib "2d2" on scm "5b1" on unix
Function:slib:report
Displays the information of (slib:report-version) followed by
almost all the information neccessary for submitting a problem report.
An unspecified value is returned.
Function:slib:report#t
provides a more verbose listing.
Function:slib:reportfilename
Writes the report to file `filename'.
(slib:report)
=>
slib "2d2" on scm "5b1" on unix
(implementation-vicinity) is "/home/jaffer/scm/"
(library-vicinity) is "/home/jaffer/slib/"
(scheme-file-suffix) is ".scm"
loaded *features* :
trace alist qp sort
common-list-functions macro values getopt
compiled
implementation *features* :
bignum complex real rational
inexact vicinity ed getenv
tmpnam abort transcript with-file
ieee-p1178 rev4-report rev4-optional-procedures hash
object-hash delay eval dynamic-wind
multiarg-apply multiarg/and- logical defmacro
string-port source current-time record
rev3-procedures rev2-procedures sun-dl string-case
array dump char-ready? full-continuation
system
implementation *catalog* :
(i/o-extensions compiled "/home/jaffer/scm/ioext.so")
...
These procedures are provided by all implementations.
Procedure:file-exists?filename
Returns #t if the specified file exists. Otherwise, returns
#f. If the underlying implementation does not support this
feature then #f is always returned.
Procedure:delete-filefilename
Deletes the file specified by filename. If filename can not
be deleted, #f is returned. Otherwise, #t is
returned.
Procedure:tmpnam
Returns a pathname for a file which will likely not be used by any other
process. Successive calls to (tmpnam) will return different
pathnames.
Procedure:current-error-port
Returns the current port to which diagnostic and error output is
directed.
Procedure:force-output
Procedure:force-outputport
Forces any pending output on port to be delivered to the output
device and returns an unspecified value. The port argument may be
omitted, in which case it defaults to the value returned by
(current-output-port).
Procedure:output-port-width
Procedure:output-port-widthport
Returns the width of port, which defaults to
(current-output-port) if absent. If the width cannot be
determined 79 is returned.
Procedure:output-port-height
Procedure:output-port-heightport
Returns the height of port, which defaults to
(current-output-port) if absent. If the height cannot be
determined 24 is returned.
The following procedures were present in Scheme until R4RS
(see section `Language changes' in Revised(4) Scheme).
They are provided by all SLIB implementations.
These procedures are provided by all implementations.
Procedure:slib:load-sourcename
Loads a file of Scheme source code from name with the default
filename extension used in SLIB. For instance if the filename extension
used in SLIB is `.scm' then (slib:load-source "foo") will
load from file `foo.scm'.
Procedure:slib:load-compiledname
On implementations which support separtely loadable compiled modules,
loads a file of compiled code from name with the implementation's
filename extension for compiled code appended.
Procedure:slib:loadname
Loads a file of Scheme source or compiled code from name with the
appropriate suffixes appended. If both source and compiled code are
present with the appropriate names then the implementation will load
just one. It is up to the implementation to choose which one will be
loaded.
If an implementation does not support compiled code then
slib:load will be identical to slib:load-source.
Procedure:slib:evalobj
eval returns the value of obj evaluated in the current top
level environment. 6.4.10 Eval provides a more general evaluation
facility.
Procedure:slib:eval-loadfilename eval
filename should be a string. If filename names an existing file,
the Scheme source code expressions and definitions are read from the
file and eval called with them sequentially. The
slib:eval-load procedure does not affect the values returned by
current-input-port and current-output-port.
Procedure:slib:warnarg1 arg2 ...
Outputs a warning message containing the arguments.
Procedure:slib:errorarg1 arg2 ...
Outputs an error message containing the arguments, aborts evaluation of
the current form and responds in a system dependent way to the error.
Typical responses are to abort the program or to enter a read-eval-print
loop.
Procedure:slib:exitn
Procedure:slib:exit
Exits from the Scheme session returning status n to the system.
If n is omitted or #t, a success status is returned to the
system (if possible). If n is #f a failure is returned to
the system (if possible). If n is an integer, then n is
returned to the system (if possible). If the Scheme session cannot exit
an unspecified value is returned from slib:exit.
Entries that are labeled as Functions are called for their return
values. Entries that are labeled as Procedures are called primarily for
their side effects.
Examples in this text were produced using the scm Scheme
implementation.
At the beginning of each section, there is a line that looks like
(require 'feature). Include this line in your code prior to
using the package.
Returns a new (interned) symbol each time it is called. The symbol
names are implementation-dependent
(gentemp) => scm:G0
(gentemp) => scm:G1
Function:defmacro:evale
Returns the slib:eval of expanding all defmacros in scheme
expression e.
Function:defmacro:loadfilename
filename should be a string. If filename names an existing file,
the defmacro:load procedure reads Scheme source code expressions
and definitions from the file and evaluates them sequentially. These
source code expressions and definitions may contain defmacro
definitions. The macro:load procedure does not affect the values
returned by current-input-port and
current-output-port.
Function:defmacro?sym
Returns #t if sym has been defined by defmacro,
#f otherwise.
Function:macroexpand-1form
Function:macroexpandform
If form is a macro call, macroexpand-1 will expand the
macro call once and return it. A form is considered to be a macro
call only if it is a cons whose car is a symbol for which a
defmacro has been defined.
macroexpand is similar to macroexpand-1, but repeatedly
expands form until it is no longer a macro call.
Macro:defmacroname lambda-list form ...
When encountered by defmacro:eval, defmacro:macroexpand*,
or defmacro:load defines a new macro which will henceforth be
expanded when encountered by defmacro:eval,
defmacro:macroexpand*, or defmacro:load.
(require 'macro) is the appropriate call if you want R4RS
high-level macros but don't care about the low level implementation. If
an SLIB R4RS macro implementation is already loaded it will be used.
Otherwise, one of the R4RS macros implemetations is loaded.
The SLIB R4RS macro implementations support the following uniform
interface:
Function:macro:expandsexpression
Takes an R4RS expression, macro-expands it, and returns the result of
the macro expansion.
Function:macro:evalsexpression
Takes an R4RS expression, macro-expands it, evals the result of the
macro expansion, and returns the result of the evaluation.
Procedure:macro:loadfilename
filename should be a string. If filename names an existing file,
the macro:load procedure reads Scheme source code expressions and
definitions from the file and evaluates them sequentially. These source
code expressions and definitions may contain macro definitions. The
macro:load procedure does not affect the values returned by
current-input-port and current-output-port.
These macros are not referentially transparent (see section `Macros' in Revised(4) Scheme). Lexically scoped macros (i.e., let-syntax
and letrec-syntax) are not supported. In any case, the problem
of referential transparency gains poignancy only when let-syntax
and letrec-syntax are used. So you will not be courting
large-scale disaster unless you're using system-function names as local
variables with unintuitive bindings that the macro can't use. However,
if you must have the full r4rs macro functionality, look to the
more featureful (but also more expensive) versions of syntax-rules
available in slib 2.4 Macros That Work, 2.5 Syntactic Closures, and
2.6 Syntax-Case Macros.
Macro:define-syntaxkeyword transformer-spec
The keyword is an identifier, and the transformer-spec
should be an instance of syntax-rules.
The top-level syntactic environment is extended by binding the
keyword to the specified transformer.
literals is a list of identifiers, and each syntax-rule
should be of the form
(patterntemplate)
where the pattern and template are as in the grammar above.
An instance of syntax-rules produces a new macro transformer by
specifying a sequence of hygienic rewrite rules. A use of a macro whose
keyword is associated with a transformer specified by
syntax-rules is matched against the patterns contained in the
syntax-rules, beginning with the leftmost syntax-rule.
When a match is found, the macro use is trancribed hygienically
according to the template.
Each pattern begins with the keyword for the macro. This keyword is not
involved in the matching and is not considered a pattern variable or
literal identifier.
Macros That Work differs from the other R4RS macro
implementations in that it does not expand derived expression types to
primitive expression types.
Function:macro:expandexpression
Function:macwork:expandexpression
Takes an R4RS expression, macro-expands it, and returns the result of
the macro expansion.
Function:macro:evalexpression
Function:macwork:evalexpression
macro:eval returns the value of expression in the current
top level environment. expression can contain macro definitions.
Side effects of expression will affect the top level
environment.
Procedure:macro:loadfilename
Procedure:macwork:loadfilename
filename should be a string. If filename names an existing file,
the macro:load procedure reads Scheme source code expressions and
definitions from the file and evaluates them sequentially. These source
code expressions and definitions may contain macro definitions. The
macro:load procedure does not affect the values returned by
current-input-port and current-output-port.
References:
The Revised^4 Report on the Algorithmic Language Scheme Clinger
and Rees [editors]. To appear in LISP Pointers. Also available as a
technical report from the University of Oregon, MIT AI Lab, and
Cornell.
Macros That Work. Clinger and Rees. POPL '91.
The supported syntax differs from the R4RS in that vectors are allowed
as patterns and as templates and are not allowed as pattern or template
data.
transformer spec ==> (syntax-rules literals rules)
rules ==> ()
| (rule . rules)
rule ==> (pattern template)
pattern ==> pattern_var ; a symbol not in literals
| symbol ; a symbol in literals
| ()
| (pattern . pattern)
| (ellipsis_pattern)
| #(pattern*) ; extends R4RS
| #(pattern* ellipsis_pattern) ; extends R4RS
| pattern_datum
template ==> pattern_var
| symbol
| ()
| (template2 . template2)
| #(template*) ; extends R4RS
| pattern_datum
template2 ==> template
| ellipsis_template
pattern_datum ==> string ; no vector
| character
| boolean
| number
ellipsis_pattern ==> pattern ...
ellipsis_template ==> template ...
pattern_var ==> symbol ; not in literals
literals ==> ()
| (symbol . literals)
No pattern variable appears more than once within a pattern.
For every occurrence of a pattern variable within a template, the
template rank of the occurrence must be greater than or equal to the
pattern variable's rank.
Every ellipsis template must open at least one variable.
For every ellipsis template, the variables opened by an ellipsis
template must all be bound to sequences of the same length.
The pattern variables associated with an ellipsis pattern are the
variables bound by the pattern, and the pattern variables associated
with an ellipsis template are the variables opened by the ellipsis
template.
If the template contains a big chunk that contains no pattern variables
or inserted identifiers, then the big chunk will be copied
unnecessarily. That shouldn't matter very often.
Returns scheme code with the macros and derived expression types of
expression expanded to primitive expression types.
Function:macro:evalexpression
Function:synclo:evalexpression
macro:eval returns the value of expression in the current
top level environment. expression can contain macro definitions.
Side effects of expression will affect the top level
environment.
Procedure:macro:loadfilename
Procedure:synclo:loadfilename
filename should be a string. If filename names an existing file,
the macro:load procedure reads Scheme source code expressions and
definitions from the file and evaluates them sequentially. These
source code expressions and definitions may contain macro definitions.
The macro:load procedure does not affect the values returned by
current-input-port and current-output-port.
This document describes syntactic closures, a low-level macro
facility for the Scheme programming language. The facility is an
alternative to the low-level macro facility described in the
Revised^4 Report on Scheme. This document is an addendum to that
report.
The syntactic closures facility extends the BNF rule for
transformer spec to allow a new keyword that introduces a
low-level macro transformer:
The description of the facility is divided into three parts. The first
part defines basic terminology. The second part describes how macro
transformers are defined. The third part describes the use of
identifiers, which extend the syntactic closure mechanism to be
compatible with syntax-rules.
This section defines the concepts and data types used by the syntactic
closures facility.
Forms are the syntactic entities out of which programs are
recursively constructed. A form is any expression, any definition, any
syntactic keyword, or any syntactic closure. The variable name that
appears in a set! special form is also a form. Examples of
forms:
17
#t
car
(+ x 4)
(lambda (x) x)
(define pi 3.14159)
if
define
An alias is an alternate name for a given symbol. It can
appear anywhere in a form that the symbol could be used, and when quoted
it is replaced by the symbol; however, it does not satisfy the predicate
symbol?. Macro transformers rarely distinguish symbols from
aliases, referring to both as identifiers.
A syntactic environment maps identifiers to their
meanings. More precisely, it determines whether an identifier is a
syntactic keyword or a variable. If it is a keyword, the meaning is an
interpretation for the form in which that keyword appears. If it is a
variable, the meaning identifies which binding of that variable is
referenced. In short, syntactic environments contain all of the
contextual information necessary for interpreting the meaning of a
particular form.
A syntactic closure consists of a form, a syntactic
environment, and a list of identifiers. All identifiers in the form
take their meaning from the syntactic environment, except those in the
given list. The identifiers in the list are to have their meanings
determined later. A syntactic closure may be used in any context in
which its form could have been used. Since a syntactic closure is also
a form, it may not be used in contexts where a form would be illegal.
For example, a form may not appear as a clause in the cond special form.
A syntactic closure appearing in a quoted structure is replaced by its
form.
This section describes the transformer special form and the
procedures make-syntactic-closure and
capture-syntactic-environment.
Syntax:transformerexpression
Syntax: It is an error if this syntax occurs except as a
transformer spec.
Semantics: The expression is evaluated in the standard transformer
environment to yield a macro transformer as described below. This macro
transformer is bound to a macro keyword by the special form in which the
transformer expression appears (for example,
let-syntax).
A macro transformer is a procedure that takes two arguments, a
form and a syntactic environment, and returns a new form. The first
argument, the input form, is the form in which the macro keyword
occurred. The second argument, the usage environment, is the
syntactic environment in which the input form occurred. The result of
the transformer, the output form, is automatically closed in the
transformer environment, which is the syntactic environment in
which the transformer expression occurred.
For example, here is a definition of a push macro using
syntax-rules:
In this example, the identifiers set! and cons are closed
in the transformer environment, and thus will not be affected by the
meanings of those identifiers in the usage environment
env.
Some macros may be non-hygienic by design. For example, the following
defines a loop macro that implicitly binds exit to an escape
procedure. The binding of exit is intended to capture free
references to exit in the body of the loop, so exit must
be left free when the body is closed:
To assign meanings to the identifiers in a form, use
make-syntactic-closure to close the form in a syntactic
environment.
Function:make-syntactic-closureenvironment free-names form
environment must be a syntactic environment, free-names must
be a list of identifiers, and form must be a form.
make-syntactic-closure constructs and returns a syntactic closure
of form in environment, which can be used anywhere that
form could have been used. All the identifiers used in
form, except those explicitly excepted by free-names, obtain
their meanings from environment.
Here is an example where free-names is something other than the
empty list. It is instructive to compare the use of free-names in
this example with its use in the loop example above: the examples
are similar except for the source of the identifier being left
free.
let1 is a simplified version of let that only binds a
single identifier, and whose body consists of a single expression. When
the body expression is syntactically closed in its original syntactic
environment, the identifier that is to be bound by let1 must be
left free, so that it can be properly captured by the lambda in
the output form.
To obtain a syntactic environment other than the usage environment, use
capture-syntactic-environment.
Function:capture-syntactic-environmentprocedure
capture-syntactic-environment returns a form that will, when
transformed, call procedure on the current syntactic environment.
procedure should compute and return a new form to be transformed,
in that same syntactic environment, in place of the form.
An example will make this clear. Suppose we wanted to define a simple
loop-until keyword equivalent to
(define-syntax loop-until
(syntax-rules ()
((loop-until id init test return step)
(letrec ((loop
(lambda (id)
(if test return (loop step)))))
(loop init)))))
The following attempt at defining loop-until has a subtle bug:
This definition appears to take all of the proper precautions to prevent
unintended captures. It carefully closes the subexpressions in their
original syntactic environment and it leaves the id identifier
free in the test, return, and step expressions, so
that it will be captured by the binding introduced by the lambda
expression. Unfortunately it uses the identifiers if and
loop within that lambda expression, so if the user of
loop-until just happens to use, say, if for the
identifier, it will be inadvertently captured.
The syntactic environment that if and loop want to be
exposed to is the one just outside the lambda expression: before
the user's identifier is added to the syntactic environment, but after
the identifier loop has been added.
capture-syntactic-environment captures exactly that environment
as follows:
In this case, having captured the desired syntactic environment, it is
convenient to construct syntactic closures of the identifiers if
and the loop and use them in the body of the
lambda.
A common use of capture-syntactic-environment is to get the
transformer environment of a macro transformer:
This section describes the procedures that create and manipulate
identifiers. Previous syntactic closure proposals did not have an
identifier data type -- they just used symbols. The identifier data
type extends the syntactic closures facility to be compatible with the
high-level syntax-rules facility.
As discussed earlier, an identifier is either a symbol or an
alias. An alias is implemented as a syntactic closure whose
form is an identifier:
(make-syntactic-closure env '() 'a)
=> an alias
Aliases are implemented as syntactic closures because they behave just
like syntactic closures most of the time. The difference is that an
alias may be bound to a new value (for example by lambda or
let-syntax); other syntactic closures may not be used this way.
If an alias is bound, then within the scope of that binding it is looked
up in the syntactic environment just like any other identifier.
Aliases are used in the implementation of the high-level facility
syntax-rules. A macro transformer created by syntax-rules
uses a template to generate its output form, substituting subforms of
the input form into the template. In a syntactic closures
implementation, all of the symbols in the template are replaced by
aliases closed in the transformer environment, while the output form
itself is closed in the usage environment. This guarantees that the
macro transformation is hygienic, without requiring the transformer to
know the syntactic roles of the substituted input subforms.
Function:identifier?object
Returns #t if object is an identifier, otherwise returns
#f. Examples:
The predicate eq? is used to determine if two identifers are
"the same". Thus eq? can be used to compare identifiers
exactly as it would be used to compare symbols. Often, though, it is
useful to know whether two identifiers "mean the same thing". For
example, the cond macro uses the symbol else to identify
the final clause in the conditional. A macro transformer for
cond cannot just look for the symbol else, because the
cond form might be the output of another macro transformer that
replaced the symbol else with an alias. Instead the transformer
must look for an identifier that "means the same thing" in the usage
environment as the symbol else means in the transformer
environment.
environment1 and environment2 must be syntactic
environments, and identifier1 and identifier2 must be
identifiers. identifier=? returns #t if the meaning of
identifier1 in environment1 is the same as that of
identifier2 in environment2, otherwise it returns #f.
Examples:
The syntactic closures facility was invented by Alan Bawden and Jonathan
Rees. The use of aliases to implement syntax-rules was invented
by Alan Bawden (who prefers to call them synthetic names). Much
of this proposal is derived from an earlier proposal by Alan
Bawden.
Returns scheme code with the macros and derived expression types of
expression expanded to primitive expression types.
Function:macro:evalexpression
Function:syncase:evalexpression
macro:eval returns the value of expression in the current
top level environment. expression can contain macro definitions.
Side effects of expression will affect the top level
environment.
Procedure:macro:loadfilename
Procedure:syncase:loadfilename
filename should be a string. If filename names an existing file,
the macro:load procedure reads Scheme source code expressions and
definitions from the file and evaluates them sequentially. These
source code expressions and definitions may contain macro definitions.
The macro:load procedure does not affect the values returned by
current-input-port and current-output-port.
This is version 2.1 of syntax-case, the low-level macro facility
proposed and implemented by Robert Hieb and R. Kent Dybvig.
This version is further adapted by Harald Hanche-Olsen
<hanche @ imf.unit.no> to make it compatible with, and easily usable
with, SLIB. Mainly, these adaptations consisted of:
Removing white space from `expand.pp' to save space in the
distribution. This file is not meant for human readers anyway...
Removed a couple of Chez scheme dependencies.
Renamed global variables used to minimize the possibility of name
conflicts.
Adding an SLIB-specific initialization file.
Removing a couple extra files, most notably the documentation (but see
below).
If you wish, you can see exactly what changes were done by reading the
shell script in the file `syncase.sh'.
The two PostScript files were omitted in order to not burden the SLIB
distribution with them. If you do intend to use syntax-case,
however, you should get these files and print them out on a PostScript
printer. They are available with the original syntax-case
distribution by anonymous FTP in
`cs.indiana.edu:/pub/scheme/syntax-case'.
In order to use syntax-case from an interactive top level, execute:
All R4RS syntactic forms are defined, including delay. Along
with delay are simple definitions for make-promise (into
which delay expressions expand) and force.
syntax-rules and with-syntax (described in TR356)
are defined.
syntax-case is actually defined as a macro that expands into
calls to the procedure syntax-dispatch and the core form
syntax-lambda; do not redefine these names.
Several other top-level bindings not documented in TR356 are created:
the "hooks" in `hooks.ss'
the build- procedures in `output.ss'
expand-syntax (the expander)
The syntax of define has been extended to allow (define id),
which assigns id to some unspecified value.
We have attempted to maintain R4RS compatibility where possible. The
incompatibilities should be confined to `hooks.ss'. Please let us
know if there is some incompatibility that is not flagged as such.
Send bug reports, comments, suggestions, and questions to Kent Dybvig
(dyb @ iuvax.cs.indiana.edu).
Included with the syntax-case files was `structure.scm'
which defines a macro define-structure. There is no
documentation for this macro and it is not used by any code in SLIB.
The inits are evaluated in the current environment (in some
unspecified order), the current values of the variables are saved,
the results are assigned to the variables, the expressions
are evaluated sequentially in the current environment, the
variables are restored to their original values, and the value of
the last expression is returned.
The syntax of this special form is similar to that of let, but
fluid-let temporarily rebinds existing variables. Unlike
let, fluid-let creates no new bindings; instead it
assigns the values of each init to the binding (determined
by the rules of lexical scoping) of its corresponding
variable.
`Yet Another Scheme Object System' is a simple object system for Scheme
based on the paper by Norman Adams and Jonathan Rees: Object
Oriented Programming in Scheme, Proceedings of the 1988 ACM Conference
on LISP and Functional Programming, July 1988 [ACM #552880].
The object system supports multiple inheritance. An instance can
inherit from 0 or more ancestors. In the case of multiple inherited
operations with the same identity, the operation used is that from the
first ancestor which contains it (in the ancestor let). An
operation may be applied to any Scheme data object--not just instances.
As code which creates instances is just code, there are no classes
and no meta-anything. Method dispatch is by a procedure call a la
CLOS rather than by send syntax a la Smalltalk.
Disclaimer:
There are a number of optimizations which can be made. This
implementation is expository (although performance should be quite
reasonable). See the L&FP paper for some suggestions.
Defines a default behavior for data objects which don't handle the
operation opname. The default behavior (for an empty
default-body) is to generate an error.
Syntax:define-predicateopname?
Defines a predicate opname?, usually used for determining the
type of an object, such that (opname?object)
returns #t if object has an operation opname? and
#f otherwise.
Syntax:object((nameselfarg ...) body) ...
Returns an object (an instance of the object system) with operations.
Invoking (nameobjectarg ... executes the
body of the object with self bound to object and
with argument(s) arg....
A let-like form of object for multiple inheritance. It
returns an object inheriting the behaviour of ancestor1 etc. An
operation will be invoked in an ancestor if the object itself does not
provide such a method. In the case of multiple inherited operations
with the same identity, the operation used is the one found in the first
ancestor in the ancestor list.
Syntax:operate-ascomponent operation self arg ...
Used in an operation definition (of self) to invoke the
operation in an ancestor component but maintain the object's
identity. Also known as "send-to-super".
Procedure:printobj port
A default print operation is provided which is just (format
portobj) (see section 3.2 Format (version 3.0)) for non-instances and prints
obj preceded by `#<INSTANCE>' for instances.
Function:sizeobj
The default method returns the number of elements in obj if it is
a vector, string or list, 2 for a pair, 1 for a character
and by default id an error otherwise. Objects such as collections
(see section 6.1.6 Collections) may override the default in an obvious way.
Setters implement generalized locations for objects
associated with some sort of mutable state. A getter operation
retrieves a value from a generalized location and the corresponding
setter operation stores a value into the location. Only the getter is
named -- the setter is specified by a procedure call as below. (Dylan
uses special syntax.) Typically, but not necessarily, getters are
access operations to extract values from Yasos objects (see section 2.8 Yasos).
Several setters are predefined, corresponding to getters car,
cdr, string-ref and vector-ref e.g., (setter
car) is equivalent to set-car!.
This implementation of setters is similar to that in Dylan(TM)
(Dylan: An object-oriented dynamic language, Apple Computer
Eastern Research and Technology). Common LISP provides similar
facilities through setf.
Function:settergetter
Returns the setter for the procedure getter. E.g., since
string-ref is the getter corresponding to a setter which is
actually string-set!:
(define foo "foo")
((setter string-ref) foo 0 #\F) ; set element 0 of foo
foo => "Foo"
Syntax:setplace new-value
If place is a variable name, set is equivalent to
set!. Otherwise, place must have the form of a procedure
call, where the procedure name refers to a getter and the call indicates
an accessible generalized location, i.e., the call would return a value.
The return value of set is usually unspecified unless used with a
setter whose definition guarantees to return a useful value.
Add procedures getter and setter to the (inaccessible) list
of valid setter/getter pairs. setter implements the store
operation corresponding to the getter access operation for the
relevant state. The return value is unspecified.
Procedure:remove-setter-forgetter
Removes the setter corresponding to the specified getter from the
list of valid setters. The return value is unspecified.
Syntax:define-access-operationgetter-name
Shorthand for a Yasos define-operation defining an operation
getter-name that objects may support to return the value of some
mutable state. The default operation is to signal an error. The return
value is unspecified.