[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
backquote
Date: Thu, 10 Nov 88 11:55:40 PST
From: Don Cohen <donc@vaxa.isi.edu>
I don't understand the 3 line explanation of backquotes in backquotes
on p. 350 of CLtL. I'd appreciate some enlightenment. Also, I'd be
interested in whether backquote is capable of solving the problem below.
I surely don't see how to do it, but since I don't really understand
embedded backquotes I'm not sure it can't be done.
The problem:
Suppose we want to declare versions of existing functions that do type
checking, e.g., we want a protected version of (+ x y) which, if x and y
are both numbers returns their sum and otherwise returns nil. This could
be done by the following macro:
(defmacro protected-+ (x y)
`(let ((x ,x) (y ,y)) (and (numberp x) (numberp y) (+ x y))))
so that
(macroexpand '(protected-+ a b))
=>
(LET ((X A) (Y B))
(AND (NUMBERP X) (NUMBERP Y) (+ X Y)))
(The let assures that each argument is evaluated only once.)
Since there are many such functions to declare, we might want a macro
to define them, e.g.,
(define-protected + (numberp x) (numberp y))
would be a reasonable way to create the macro above.
If we try to define this macro in the straight forward way we start
out like so:
(defmacro define-protected (function &rest args)
`(defmacro ,(MAKE-PROTECTED-NAME function)
,(mapcar #'cadr args)
`(let ,(BUILD-LET-LIST args)
(and ,@args
,(BUILD-FUNCTION-CALL function args)))))
Unfortunately, it appears that BUILD-LET-LIST has to build an expression
with more commas than backquotes, which the backquote readmacro cannot do.
Another way to look at it is that I don't see how one backquote can
produce different backquoted expressions with varying numbers of commas.
The problem is that you are insisting on using the same argument names
in both places. I think it becomes possible if you drop that
requirement, and just use GENSYMs. However, I'm not going to try to
work it out, because I have a better idea:
It would be easier if protected-+ were defined to be a function, and I
don't see any reason why it shouldn't be (for efficiency,
DEFINE-PROTECTED could also include an INLINE proclamation).
(defmacro define-protected (function &rest arg-specs)
(let ((protected-name (make-protected-name function))
(args (mapcar #'cadr arg-specs)))
`(progn
(proclaim '(inline ,protected-name))
(defun ,protected-name ,args
(and ,@arg-specs
(,function .,args))))))
Here's another variant. This has the improvement (in my opinion) that
you don't build in assumptions about the format of the predicate call
(the MAPCAR #'CADR means that an argument can't be described as (and
(numberp x) (plusp x)).
(defmacro define-protected (function &rest arg-specs)
"(define-protected <function> &rest (<arg1> <type1>) ...)"
(let ((protected-name (make-protected-name function))
(args (mapcar #'car arg-specs)))
`(progn
(proclaim '(inline ,protected-name))
(defun ,protected-name ,args
,@(mapcar #'(lambda (arg-spec)
`(check-type ,.arg-spec))
arg-specs)
(,function .,args)))))
This allows you to use arbitrary type specifiers (which are completely
general because of the (SATISFIES predicate) type spec). In this case,
your example above becomes:
(define-protected + (x number) (y number))
barmar
- References:
- backquote
- From: Don Cohen <donc@vaxa.isi.edu>