http://www-128.ibm.com/developerworks/linux/library/l-metaprog2.html?ca=dgr-lnxw04MetaProgP2
I would not recommend it. The macros using datum->syntax-object
in the sections "Introducing identifiers" and
"Building boilerplate macros" are seriously wrong.
Regards
Andre
Wrong as in "bad style" or "leads to undefined/unintended results"? I
mention the problems in introducing identifiers in the article, yet I
still maintain that it has many valid uses.
Jon
----
Learn to program using Linux assembly language
http://www.cafeshops.com/bartlettpublish.8640017
> Wrong as in "bad style" or "leads to undefined/unintended results"?
Wrong as in "leads to undefined/unintended results". The identifiers
are not introduced correctly. The example:
(define-syntax with-math-defines
(lambda (x)
(syntax-case x ()
((with-math-defines expression)
(with-syntax
((expr
(datum->syntax-object
(syntax k)
`(let ( (pi 3.14) (e 2.72))
,(syntax-object->datum (syntax expression))))))
(syntax expr))))))
has two things wrong with it. First, the k in (syntax k) should
refer to the context where the macro call occurred (otherwise the
macro will break when using modules, etc.). Fixing this, let's write:
(define-syntax with-math-defines
(lambda (x)
(syntax-case x ()
((k expression)
(with-syntax
((expr
(datum->syntax-object
(syntax k)
`(let ( (pi 3.14) (e 2.72))
,(syntax-object->datum (syntax expression))))))
(syntax expr))))))
But now we have lost referential transparency. For example,
the following give errors:
(let ((let 0))
(with-math-defines
(* pi e)))
==> procedure application: expected procedure,
given: 3.141592653589793
(let ((x 0))
(let-syntax ((bar (syntax-rules ()
((_ exp)
(with-math-defines exp)))))
(let ((x 1))
(let-syntax ((foo (syntax-rules ()
((_) (bar x)))))
(foo)))))
==> 0 instead of the correct answer 1
The correct way to introduce these variables, while preserving
hygiene and referential transparency, is
(define-syntax with-math-defines
(lambda (x)
(syntax-case x ()
((k expression)
(with-syntax
((pi (datum->syntax-object (syntax k) 'pi))
(e (datum->syntax-object (syntax k) 'e)))
(syntax
(let ((pi 3.14) (e 2.72))
expression)))))))
(let ((let 0))
(with-math-defines
(* pi e))) ==> 8.5408
(let ((x 0))
(let-syntax ((bar (syntax-rules ()
((_ exp)
(with-math-defines exp)))))
(let ((x 1))
(let-syntax ((foo (syntax-rules ()
((_) (bar x)))))
(foo))))) ==> 1
Similar considerations apply to the cgi-boilerplate macro.
Regards
Andre
Related to this, there are a couple of places where the article gives a
misleading description of the meaning of (syntax k). First:
"...(syntax k) which is a little magic formula that helps the syntax
converter get the context correct"
This should not imply that (syntax k) will always work as intended,
regardless of what "k" happens to be.
The other one is:
"Also note that the expression (syntax k) in datum->syntax-object is
necessary but essentially meaningless. It is used to invoke a little bit
of "magic" within the syntax processor so that the datum->syntax-object
function will know what context the expression should be processed in.
It is always written as (syntax k)."
I presume that the conclusion that "It is always written as (syntax k)"
was based on the four instances of (syntax k) in TSPL chapter 8, but
note that those all use k as the pattern variable corresponding to the
macro name.
Andre gave a succinct and correct description: "the k in (syntax k)
should refer to the context where the macro call occurred". It is not
meaningless, since hygiene will not work properly if it is not correct.
To demonstrate the problem, here's a simple example of a macro that
takes its input, converts it to a datum, and back to a syntax object:
(define-syntax foo
(lambda (stx)
(syntax-case stx ()
((foo a) (datum->syntax-object
(syntax k)
(syntax-object->datum (syntax a)))))))
Here's a transcript of its use in MzScheme (the same behavior occurs in
Chez, Chicken, Gambit, and SISC) :
> (foo 23)
23
> (define p 23)
> (foo p)
23
> (define a 23)
> (foo a)
compile: identifier used out of context in: a
In the last test, the 'a' in the calling environment conflicted with the
pattern variable named 'a' in the macro. This can be fixed by ensuring
that (syntax k) references a pattern variable -- either change the
pattern (foo a) to (k a), or change (syntax k) to either (syntax foo) or
(syntax a).
Anton
> Wrong as in "bad style" or "leads to undefined/unintended results"?
Another, subtler, example of unintended results due to misuse
of the context argument k: The original macro
(define-syntax with-math-defines
(lambda (x)
(syntax-case x ()
((with-math-defines expression)
(with-syntax
((expr
(datum->syntax-object
(syntax k)
`(let ( (pi 3.14) (e 2.72))
,(syntax-object->datum (syntax expression))))))
(syntax expr))))))
gives for the following:
(let ((x 0))
(let-syntax ((bar (syntax-rules ()
((_)
(with-math-defines x)))))
(bar)))
==> compile: identifier used out of context in: x
(instead of the correct answer 0)
What is happening is that it is converting the x in the input
to the same x that appears in (syntax-case x () ....). These
variables exisit during different phases (expansion/execution),
causing a phase error.
Regards
Andre
> Jonathan Bartlett wrote:
> Another, subtler, example of unintended results due to misuse
> of the context argument k:
... or even simpler:
(define x 0)
(with-math-defines x)
==> compile: identifier used out of context in: x
Andre
Thanks so much!
Jon