From 725fd9806aebf648464dcd3d52c10ade17d956ab Mon Sep 17 00:00:00 2001 From: Neil Jerram Date: Sat, 5 May 2001 13:40:18 +0000 Subject: [PATCH] * New material on macros. --- doc/ChangeLog | 8 ++ doc/scheme-data.texi | 21 +-- doc/scheme-procedures.texi | 264 ++++++++++++++++++++++++++++++++----- 3 files changed, 250 insertions(+), 43 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 5bbc58bd3..24f5e991e 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,11 @@ +2001-05-05 Neil Jerram + + * 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 * new-docstrings.texi, posix.texi, scheme-control.texi, diff --git a/doc/scheme-data.texi b/doc/scheme-data.texi index a541752ee..7c5b649ca 100755 --- a/doc/scheme-data.texi +++ b/doc/scheme-data.texi @@ -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 diff --git a/doc/scheme-procedures.texi b/doc/scheme-procedures.texi index b526bf7e7..62d99e519 100644 --- a/doc/scheme-procedures.texi +++ b/doc/scheme-procedures.texi @@ -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 ) + +makes the into a macro so that + + ( ...) + +expands at _compile_ or _read_ time (i.e. before any +evaluation begins) into some expression that is +given by the . + + +@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