summaryrefslogtreecommitdiff
path: root/doc/ref/api-macros.texi
diff options
context:
space:
mode:
authorIan Price <ianprice90@googlemail.com>2012-01-07 01:59:33 +0000
committerAndy Wingo <wingo@pobox.com>2012-01-08 13:28:35 +0100
commit729b62bd95c61b6e2992f823b469b76bdc483b0d (patch)
treecfb7667846e6bc4ee629ac08f87ec4225b3925b9 /doc/ref/api-macros.texi
parent49d09292ac6d92a86b574478a69450b85d2dd8c1 (diff)
downloadguile-729b62bd95c61b6e2992f823b469b76bdc483b0d.tar.gz
document syntax parameters
* doc/ref/api-macros.texi (Macros): Add subsection for "Syntax Parameters"
Diffstat (limited to 'doc/ref/api-macros.texi')
-rw-r--r--doc/ref/api-macros.texi76
1 files changed, 76 insertions, 0 deletions
diff --git a/doc/ref/api-macros.texi b/doc/ref/api-macros.texi
index 92816ad82..7ff5bf9bc 100644
--- a/doc/ref/api-macros.texi
+++ b/doc/ref/api-macros.texi
@@ -40,6 +40,7 @@ languages}, or EDSLs.}.
* Syntax Case:: Procedural, hygienic macros.
* Defmacros:: Lisp-style macros.
* Identifier Macros:: Identifier macros.
+* Syntax Parameters:: Syntax Parameters
* Eval When:: Affecting the expand-time environment.
* Internal Macros:: Macros as first-class values.
@end menu
@@ -861,6 +862,81 @@ wrapping in @code{#'} syntax forms.
@end deffn
+@node Syntax Parameters
+@subsection Syntax Parameters
+
+Syntax parameters@footnote{Described in the paper @cite{Keeping it Clean with
+Syntax Parameters} by Barzilay, Culpepper and Flatt.} are a mechanism for rebinding a macro
+definition within the dynamic extent of a macro expansion. It provides
+a convenient solution to one of the most common types of unhygienic
+macro: those that introduce a unhygienic binding each time the macro
+is used. Examples include a @code{lambda} form with a @code{return} keyword, or
+class macros that introduce a special @code{self} binding.
+
+With syntax parameters, instead of introducing the binding
+unhygienically each time, we instead create one binding for the
+keyword, which we can then adjust later when we want the keyword to
+have a different meaning. As no new bindings are introduced, hygiene
+is preserved. This is similar to the dynamic binding mechanisms we
+have at run-time like @ref{SRFI-39, parameters} or
+@ref{Fluids and Dynamic States, fluids}, except that the dynamic
+binding only occurs during macro expansion. The code after macro
+expansion remains lexically scoped.
+
+@deffn {Syntax} define-syntax-parameter keyword transformer
+Binds @var{keyword} to the value obtained by evaluating @var{transformer}. The
+@var{transformer} provides the default expansion for the syntax parameter,
+and in the absence of @code{syntax-parameterize}, is functionally equivalent
+to @code{define-syntax}. Usually, you will just want to have the @var{transformer}
+throw a syntax error indicating that the @var{keyword} is supposed to be
+used in conjunction with another macro, for example:
+@example
+(define-syntax-parameter return
+ (lambda (stx)
+ (syntax-violation 'return "return used outside of a lambda^" stx)))
+@end example
+@end deffn
+
+@deffn {Syntax} syntax-parameterize ((keyword transformer) @dots{}) exp @dots{}
+Adjusts @var{keyword} @dots{} to use the values obtained by evaluating
+their @var{transformer} @dots{}, in the expansion of the @var{exp} @dots{}
+forms. Each @var{keyword} must be bound to a
+syntax-parameter. @code{syntax-parameterize} differs from
+@code{let-syntax}, in that the binding is not shadowed, but adjusted,
+and so uses of the keyword in the expansion of exp forms use the new
+transformers. This is somewhatsimilar to how @code{parameterize}
+adjusts the values of regular parameters, rather than creating new
+bindings.
+
+@example
+(define-syntax lambda^
+ (syntax-rules ()
+ [(lambda^ argument-list body bodies ...)
+ (lambda argument-list
+ (call-with-current-continuation
+ (lambda (escape)
+ ;; in the body we adjust the 'return' keyword so that calls
+ ;; to 'return' are replaced with calls to the escape continuation
+ (syntax-parameterize ([return (syntax-rules ()
+ [(return vals (... ...))
+ (escape vals (... ...))])])
+ body
+ bodies ...))))]))
+
+;; now we can write functions that return early. Here, 'product' will
+;; return immediately if it sees any 0 element.
+(define product
+ (lambda^ (list)
+ (fold (lambda (n o)
+ (if (zero? n)
+ (return 0)
+ (* n o)))
+ 1
+ list)))
+@end example
+@end deffn
+
+
@node Eval When
@subsection Eval-when