Prev Up Next
A two-argument disjunction form, my-or, could be
defined as follows:
(define-macro my-or
(lambda (x y)
`(if ,x ,x ,y)))
my-or takes two arguments and returns the value of
the first of them that is true (ie, non-#f). In
particular, the second argument is evaluated only if
the first turns out to be false.
(my-or 1 2)
=> 1
(my-or #f 2)
=> 2
There is a problem with the my-or macro as it is
written. It re-evaluates the first argument if it is
true: once in the if-test, and once again in the
``then'' branch. This can cause undesired behavior if
the first argument were to contain side-effects, eg,
(my-or
(begin
(display "doing first argument")
(newline)
#t)
2)
displays "doing first argument" twice.
This can be avoided by storing
the if-test result in a local variable:
(define-macro my-or
(lambda (x y)
`(let ((temp ,x))
(if temp temp ,y))))
This is almost OK, except in the case where the
second argument happens to contain the same
identifier temp as used in the macro definition.
Eg,
(define temp 3)
(my-or #f temp)
=> #f
Surely it should be 3! The fiasco happens because
the macro uses a local variable temp to store the
value of the first argument (#f) and the
variable
temp in the second argument got captured by
the
temp introduced by the macro.
To avoid this, we need to be careful in choosing local
variables inside macro definitions. We could choose
outlandish names for such variables and hope fervently
that nobody else comes up with them. Eg,
(define-macro my-or
(lambda (x y)
`(let ((+temp ,x))
(if +temp +temp ,y))))
This will work given the tacit understanding
that +temp will not be used by code outside the
macro. This is of course an understanding waiting to
be disillusioned.
A more reliable, if verbose, approach is to use generated symbols that are guaranteed not to be
obtainable by other means. The procedure gensym
generates unique symbols each time it is called. Here
is a safe definition for my-or using gensym:
(define-macro my-or
(lambda (x y)
(let ((temp (gensym)))
`(let ((,temp ,x))
(if ,temp ,temp ,y)))))
In the macros defined in this document, in order to be
concise, we will not use the gensym approach.
Instead, we will consider the point about variable
capture as having been made, and go ahead with the less
cluttered +-as-prefix approach. We will leave it
to the astute reader to remember to convert these
+-identifiers into gensyms in the manner outlined
above.
Prev Up Next