1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-11 22:31:12 +02:00

* New material on macros.

This commit is contained in:
Neil Jerram 2001-05-05 13:40:18 +00:00
parent b6f04d92f7
commit 725fd9806a
3 changed files with 250 additions and 43 deletions

View file

@ -1,3 +1,11 @@
2001-05-05 Neil Jerram <neil@ossau.uklinux.net>
* scheme-procedures.texi (Macros, Syntax Rules, Internal Macros):
New material.
(Syntax Case): New node, but currently empty.
* scheme-data.texi (Booleans, Symbols): Supply cross-references.
2001-05-04 Neil Jerram <neil@ossau.uklinux.net>
* new-docstrings.texi, posix.texi, scheme-control.texi,

View file

@ -69,7 +69,8 @@ The two boolean values are @code{#t} for true and @code{#f} for false.
Boolean values are returned by predicate procedures, such as the general
equality predicates @code{eq?}, @code{eqv?} and @code{equal?}
(@pxref{Equality}) and numerical and string comparison operators like
@code{string=?} (REFFIXME) and @code{<=} (REFFIXME).
@code{string=?} (@pxref{String Comparison}) and @code{<=}
(@pxref{Comparison}).
@lisp
(<= 3 8)
@ -89,8 +90,8 @@ equality predicates @code{eq?}, @code{eqv?} and @code{equal?}
#t
@end lisp
In test condition contexts like @code{if} (REFFIXME) and @code{cond}
(REFFIXME), where a group of subexpressions will be evaluated only if a
In test condition contexts like @code{if} and @code{cond} (@pxref{if
cond case}), where a group of subexpressions will be evaluated only if a
@var{condition} expression evaluates to ``true'', ``true'' means any
value at all except @code{#f}.
@ -2312,9 +2313,9 @@ can be compared extremely efficiently, although they carry more
information for the human reader than, say, numbers.
It is very common in Scheme programs to use symbols as keys in
association lists (REFFIXME) or hash tables (REFFIXME), because this
usage improves the readability a lot, and does not cause any performance
loss.
association lists (@pxref{Association Lists}) or hash tables
(@pxref{Hash Tables}), because this usage improves the readability a
lot, and does not cause any performance loss.
The read syntax for symbols is a sequence of letters, digits, and
@emph{extended alphabetic characters} that begins with a character that
@ -2329,10 +2330,10 @@ they were letters. The following are extended alphabetic characters:
@end example
In addition to the read syntax defined above (which is taken from R5RS
(REFFIXME)), Guile provides a method for writing symbols with unusual
characters, such as space characters. If you (for whatever reason) need
to write a symbol containing characters not mentioned above, you write
symbols as follows:
(@pxref{Formal syntax,,,r5rs,The Revised^5 Report on Scheme})), Guile
provides a method for writing symbols with unusual characters, such as
space characters. If you (for whatever reason) need to write a symbol
containing characters not mentioned above, you write symbols as follows:
@itemize @bullet
@item

View file

@ -7,7 +7,10 @@
* Optional Arguments:: Handling keyword, optional and rest arguments.
* Procedure Properties:: Procedure properties and metainformation.
* Procedures with Setters:: Procedures with setters.
* Macros:: Macros.
* Macros:: Lisp style macro definitions.
* Syntax Rules:: Support for R5RS @code{syntax-rules}.
* Syntax Case:: Support for the @code{syntax-case} system.
* Internal Macros:: Guile's internal representation.
@end menu
@ -500,54 +503,249 @@ setter or an operator struct.
@node Macros
@section Macros
@section Lisp Style Macro Definitions
[FIXME: This needs some more text on the difference between procedures,
macros and memoizing macros. Also, any definitions listed here should
be double-checked by someone who knows what's going on. Ask Mikael, Jim
or Aubrey for help. -twp]
@cindex macros
@cindex transformation
Macros are objects which cause the expression that they appear in to be
transformed in some way @emph{before} being evaluated. In expressions
that are intended for macro transformation, the identifier that names
the relevant macro must appear as the first element, like this:
@lisp
(@var{macro-name} @var{macro-args} @dots{})
@end lisp
In Lisp-like languages, the traditional way to define macros is very
similar to procedure definitions. The key differences are that the
macro definition body should return a list that describes the
transformed expression, and that the definition is marked as a macro
definition (rather than a procedure definition) by the use of a
different definition keyword: in Lisp, @code{defmacro} rather than
@code{defun}, and in Scheme, @code{define-macro} rather than
@code{define}.
@fnindex defmacro
@fnindex define-macro
Guile supports this style of macro definition using both @code{defmacro}
and @code{define-macro}. The only difference between them is how the
macro name and arguments are grouped together in the definition:
@lisp
(defmacro @var{name} (@var{args} @dots{}) @var{body} @dots{})
@end lisp
@noindent
is the same as
@lisp
(define-macro (@var{name} @var{args} @dots{}) @var{body} @dots{})
@end lisp
@noindent
The difference is analogous to the corresponding difference between
Lisp's @code{defun} and Scheme's @code{define}.
@code{false-if-exception}, from the @file{boot-9.scm} file in the Guile
distribution, is a good example of macro definition using
@code{defmacro}:
@lisp
(defmacro false-if-exception (expr)
`(catch #t
(lambda () ,expr)
(lambda args #f)))
@end lisp
@noindent
The effect of this definition is that expressions beginning with the
identifier @code{false-if-exception} are automatically transformed into
a @code{catch} expression following the macro definition specification.
For example:
@lisp
(false-if-exception (open-input-file "may-not-exist"))
@equiv{}
(catch #t
(lambda () (open-input-file "may-not-exist"))
(lambda args #f))
@end lisp
@node Syntax Rules
@section The R5RS @code{syntax-rules} System
R5RS defines an alternative system for macro and syntax transformations
using the keywords @code{define-syntax}, @code{let-syntax},
@code{letrec-syntax} and @code{syntax-rules}.
The main difference between the R5RS system and the traditional macros
of the previous section is how the transformation is specified. In
R5RS, rather than permitting a macro definition to return an arbitrary
expression, the transformation is specified in a pattern language that
@itemize @bullet
@item
does not require complicated quoting and extraction of components of the
source expression using @code{caddr} etc.
@item
is designed such that the bindings associated with identifiers in the
transformed expression are well defined, and such that it is impossible
for the transformed expression to construct new identifiers.
@end itemize
@noindent
The last point is commonly referred to as being @dfn{hygienic}: the R5RS
@code{syntax-case} system provides @dfn{hygienic macros}.
For example, the R5RS pattern language for the @code{false-if-exception}
example of the previous section looks like this:
@lisp
(syntax-rules ()
((_ expr)
(catch #t
(lambda () expr)
(lambda args #f))))
@end lisp
In Guile, the @code{syntax-rules} system is provided by the @code{(ice-9
syncase)} module. To make these facilities available in your code,
include the expression @code{(use-modules (ice-9 syncase))} or
@code{(use-syntax (ice-9 syncase))} (@pxref{Loading Guile Modules})
before the first usage of @code{define-syntax} etc. If you are writing
a Scheme module, you can alternatively use one of the keywords
@code{#:use-module} and @code{#:use-syntax} in your @code{define-module}
declaration (@pxref{Creating Guile Modules}).
@menu
* Pattern Language:: The @code{syntax-rules} pattern language.
* Define-Syntax:: Top level syntax definitions.
* Let-Syntax:: Local syntax definitions.
@end menu
@node Pattern Language
@subsection The @code{syntax-rules} Pattern Language
@node Define-Syntax
@subsection Top Level Syntax Definitions
define-syntax: The gist is
(define-syntax <keyword> <transformer-spec>)
makes the <keyword> into a macro so that
(<keyword> ...)
expands at _compile_ or _read_ time (i.e. before any
evaluation begins) into some expression that is
given by the <transformer-spec>.
@node Let-Syntax
@subsection Local Syntax Definitions
@node Syntax Case
@section Support for the @code{syntax-case} System
@node Internal Macros
@section Internal Representation of Macros and Syntax
Internally, Guile uses three different flavours of macros. The three
flavours are called @dfn{acro} (or @dfn{syntax}), @dfn{macro} and
@dfn{mmacro}.
Given the expression
@lisp
(foo @dots{})
@end lisp
@noindent
with @code{foo} being some flavour of macro, one of the following things
will happen when the expression is evaluated.
@itemize
@item
When @code{foo} has been defined to be an @dfn{acro}, the procedure used
in the acro definition of @code{foo} is passed the whole expression and
the current lexical environment, and whatever that procedure returns is
the value of evaluating the expression. You can think of this a
procedure that receives its argument as an unevaluated expression.
@item
When @code{foo} has been defined to be a @dfn{macro}, the procedure used
in the macro definition of @code{foo} is passed the whole expression and
the current lexical environment, and whatever that procedure returns is
evaluated again. That is, the procedure should return a valid Scheme
expression.
@item
When @code{foo} has been defined to be a @dfn{mmacro}, the procedure
used in the mmacro definition of `foo' is passed the whole expression
and the current lexical environment, and whatever that procedure returns
replaces the original expression. Evaluation then starts over from the
new expression that has just been returned.
@end itemize
The key difference between a @dfn{macro} and a @dfn{mmacro} is that the
expression returned by a @dfn{mmacro} procedure is remembered (or
@dfn{memoized}) so that the expansion does not need to be done again
next time the containing code is evaluated.
The primitives @code{procedure->syntax}, @code{procedure->macro} and
@code{procedure->memoizing-macro} are used to construct acros, macros
and mmacros respectively. However, if you do not have a very special
reason to use one of these primitives, you should avoid them: they are
very specific to Guile's current implementation and therefore likely to
change. Use @code{defmacro}, @code{define-macro} (@pxref{Macros}) or
@code{define-syntax} (@pxref{Syntax Rules}) instead. (In low level
terms, @code{defmacro}, @code{define-macro} and @code{define-syntax} are
all implemented as mmacros.)
@deffn primitive procedure->syntax code
Return a @dfn{macro} which, when a symbol defined to this value
appears as the first symbol in an expression, returns the
result of applying @var{code} to the expression and the
environment.
Return a macro which, when a symbol defined to this value appears as the
first symbol in an expression, returns the result of applying @var{code}
to the expression and the environment.
@end deffn
@deffn primitive procedure->macro code
Return a @dfn{macro} which, when a symbol defined to this value
appears as the first symbol in an expression, evaluates the
result of applying @var{code} to the expression and the
environment. The value returned from @var{code} which has been
passed to @code{procedure->memoizing-macro} replaces the form
passed to @var{code}. For example:
Return a macro which, when a symbol defined to this value appears as the
first symbol in an expression, evaluates the result of applying
@var{code} to the expression and the environment. For example:
@lisp
(define trace
(procedure->macro
(lambda (x env) `(set! ,(cadr x) (tracef ,(cadr x) ',(cadr x))))))
(lambda (x env)
`(set! ,(cadr x) (tracef ,(cadr x) ',(cadr x))))))
(trace @i{foo}) @equiv{} (set! @i{foo} (tracef @i{foo} '@i{foo})).
(trace @i{foo})
@equiv{}
(set! @i{foo} (tracef @i{foo} '@i{foo})).
@end lisp
@end deffn
@deffn primitive procedure->memoizing-macro code
Return a @dfn{macro} which, when a symbol defined to this value
appears as the first symbol in an expression, evaluates the
result of applying @var{proc} to the expression and the
environment. The value returned from @var{proc} which has been
passed to @code{procedure->memoizing-macro} replaces the form
passed to @var{proc}. For example:
@lisp
(define trace
(procedure->macro
(lambda (x env) `(set! ,(cadr x) (tracef ,(cadr x) ',(cadr x))))))
(trace @i{foo}) @equiv{} (set! @i{foo} (tracef @i{foo} '@i{foo})).
@end lisp
Return a macro which, when a symbol defined to this value appears as the
first symbol in an expression, evaluates the result of applying
@var{code} to the expression and the environment.
@code{procedure->memoizing-macro} is the same as
@code{procedure->macro}, except that the expression returned by
@var{code} replaces the original macro expression in the memoized form
of the containing code.
@end deffn
In the following primitives, @dfn{acro} flavour macros are referred to
as @dfn{syntax transformers}.
@deffn primitive macro? obj
Return @code{#t} if @var{obj} is a regular macro, a memoizing macro or a
syntax transformer.
@ -556,7 +754,7 @@ syntax transformer.
@deffn primitive macro-type m
Return one of the symbols @code{syntax}, @code{macro} or
@code{macro!}, depending on whether @var{m} is a syntax
tranformer, a regular macro, or a memoizing macro,
transformer, a regular macro, or a memoizing macro,
respectively. If @var{m} is not a macro, @code{#f} is
returned.
@end deffn