jeff@aiai.ed.ac.uk (Jeff Dalton) (11/17/89)
In article <16631@bcsaic.UUCP> lbaum@bcsaic.UUCP (Larry Baum) writes: >In article <16520@bcsaic.UUCP> I stupidly wrote: >>I'll let someone else answer the general case question, but for your >>example you can easily do: >> >>(apply #'or list-of-forms) > >I hereby retract this ridiculous code: > > 1) You can't apply a macro, so this is illegal > 2) Even if it were legal it would be wrong since with OR we don't > want each form evaluated unless the preceding ones return nil. A good paper is (still) Kent Pitman's "Special Forms in Lisp" in the Proceedings of the 1980 Lisp Conference. It explains some of the background that led to Common Lisp having macros but not user- defined special forms of the FEXPR and NLAMBDA sort. It's also useful for understanding macros in general. Now to the question at hand... Several people have sent messages that give a good explanation of why macros cannot be mapped. I want to explain why sometimes they might be. I think anyone who wants to map macros (or use them as functions in other ways) should ask themselves what semantics they want to have. What is mapping COND supposed to do, for example? How about just FUNCALLing COND? Are we supposed to write something like (funcall #'cond '((test1 x) (process1 x)) '((test2 x) (process2 x))) or what? What is the result supposed to be? Normally macros just compute an expansion. That suggests that the result of the call above should be something like (if (test1 x) (process1 x) (if (test2 x) (process2 x) nil)) In Franz Lisp, macros can be applied, and the result is the expansion. However, this is presumably not the sort of semantics people want when they think of mapping OR. The point about arguments already being evaluated is related. For functions (f arg...) and (FUNCALL #'f arg...) are equivalent. Macros, however, normally work on the "source code" of their arguments, not on the values that source code might produce if it was interpreted as expressions. In (m arg...) it's the args, not the values of the args, that the macro normally precesses. But on the FUNCALL side, the args will already have been evaluated by the time the macro gets to see them. So the two expressions (one using FUNCALL, the other not) will not be equivalent for macros. Whatever semantics we give to FUNCALL of a macro, it won't be exactly the same semantics we give to FUNCALL of functions. Suppose, however, that, given some values, we could reconstruct macro arguments that the macro could process in a reasonable way. There's no good way to get from the value A to the expression (CAR '(A B C)). How would we know it was (CAR '(A B C)) and not (CADR '(B A C)), for example? But some macros don't process their arguments in a very interesting way and so may not care all that much what expression we reconstruct. In some cases, all that matters is that the expression have the right value. Then we may as well turn a value, v, into the expression (QUOTE v). In what cases will this work? Well, it won't work very well for macros like DEFUN and COND, but some macros are a lot more like functions. Suppose a macro has the following properties: (1) All the macro's arguments must be valid as expressions. This is true for AND, and OR, for example, but not for COND and DEFUN. (2) The macro treats the arguments as expressions in a function-like way. One problem is that it's not entirely clear what should count as (2). In a function call, the argument expressions are all evaluated once, but in OR, for example, they are evaluated conditionally. However, some macros are meant to have the semantics of in-line functions; and they should definitely count. Here is such a macro: (defun kar (x) `(car ,x)) Sometimes people write such macros to avoid the overhead of a function call. But then they can't use the macro with mapping functions or APPLY. Some Lisps let you define a name as both a macro and a function to get around this very problem. But such a macro _could_ be called in a reasonable way by a function like this: (defun call-macro (macro-name &rest args) (eval (cons macro-name (mapcar #'(lambda (arg) `',arg) args)))) We know that (KAR '(APPLE PIE)) evals to APPLE in two steps. First, it's expanded to get (CAR '(APPLE PIE)); then CAR's called to return APPLE. (CALL-MACRO 'KAR '(APPLE PIE)) causes CALL-MACRO to be called with two values, KAR and (APPLE PIE). CALL-MACRO then constructs the macro call (KAR '(APPLE PIE)). Here, we've got our original expression back again, but CALL-MACRO doesn't know what the original was; it would do the same for any expression whose value was (APPLE PIE). For example, (CALL-MACRO 'KAR (LIST 'APPLE 'PIE)) would also cause CALL-MACRO to construct (KAR '(APPLE PIE)). In any case, we now have a macro call that can be evaluated. And it's evaluated just as before: first it's expanded to get (CAR '(APPLE PIE)), then CAR is called. We can use the same technique to call macros like OR. Of course, OR doesn't get to conditionally evaluated the arguments to CALL-MACRO, but sometimes we wouldn't care. I believe UCI Lisp used to have a version of APPLY, called #APPLY, that could apply macros in a manner similar to this. But maybe it worked a different way. I no longer have access to a UCI Lisp to check. Jeff Dalton, JANET: J.Dalton@uk.ac.ed AI Applications Institute, ARPA: J.Dalton%uk.ac.ed@nsfnet-relay.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!J.Dalton