Evaluate the arguments of a macro form
Asked Answered
O

2

6

What is the best practice for selectively passing evaluated arguments to a macro form?

To elaborate: The usefulness of macros lies in its ability to receives unevaluated parameter, unlike the default evaluation rule for function forms. However, there is a legitimate use cases for evaluating macro arguments.

Consider a contrived example:

(defparameter *func-body* '((print i) (+ i 1)))

Suppose it would be nice that *func-body* could serve as the body of a macro our-defun that is defined as:

(defmacro our-defun (fun args &body body)
  `(defun ,fun ,args ,@body))

So after (our-defun foo (i) (+ 1 i)), we could say (foo 1) to get 2. However, if we use (our-defun foo (i) *func-body*), the result of (foo 1) will be ((PRINT I) (+ I 1)) (i.e., the value of *func-body*). It would be nice if we can force the evaluation of *func-body* as an argument to the macro our-defun.

Currently, I can think of a technique of using compile and funcall to do this, as in

(funcall (compile nil `(lambda () (our-defun foo (i) ,@*func-body*))))

after which (our-defun 1) will print out 1 and return 2, as intended. I can think of case of making this work with eval, but I would rather stay away from eval due to its peculiarity in scoping.

This leads to my question at the begining, is there a more straightforward or native way to do this?

P.S.,

A not-so-contrived example is in the function (UPDATE-HOOK), which uses two library macros (ADD-HOOK) and (REMOVE-HOOK) and needs to evaluate its parameters. The (funcall (compile nil `(lambda () ...))) technique above is used here.

(defun update-hook (hook hook-name &optional code)
  (funcall (compile nil `(lambda () (remove-hook ,hook ',hook-name))))
  (unless (null code)
    (compile hook-name `(lambda () ,@code))
    (funcall (compile nil `(lambda () (add-hook ,hook ',hook-name))))))
Olwen answered 27/3, 2013 at 21:16 Comment(0)
G
6

That's slightly confused. A macro does not receive unevaluated parameters.

A macro gets source code and creates source code from that. Remember also that source code in Lisp is actually provided as data. The macro creates code, which evaluates some forms and some not.

Macros need to work in a compiling system. Before runtime. During compile time. All the macro sees is source code and then it creates source code from that. Think of macros as code transformations, not about evaluating arguments or not.

It would be nice if we can force the evaluation of *func-body* as an argument to the macro our-defun

That is not very clean. In a compiled system, you would need to make sure that *func-body* actually has a useful binding and that it can be resolved at COMPILE TIME.

If you have a macro like DEFUN, it makes sense to have the source code static. If you want to insert some source code into a form, then it could make sense to do that at read time:

(defun foo (i) #.`(,@*foo*))

But that's code I usually would want to avoid.

two library macros (ADD-HOOK) and (REMOVE-HOOK) and needs to evaluate its parameters.

Why should ADD-HOOK and REMOVE-HOOK be macros? If you don't have a real reason, they simply should be functions. Already since they make reuse difficult.

If you want to make ADD-HOOK and REMOVE-HOOK macros for some reason, then UPDATE-HOOK usually should be a macro, too.

Guyenne answered 27/3, 2013 at 22:56 Comment(2)
Thanks for the explanation. By "unevaluated parameters," I mean macro bypasses the recursive evaluation rule that applies to function parameters and gets the raw s-exp generated from the reader. The problem with the "read-time evaluation" reader macro #. in this scenario is that, as you have observed, the expression inside it, i.e., func-body, does not have a value at read time. In this case, I did not write (add-hook) and (remove-hook). They are written by someone other than me. That is why I need to figure out a way to inject runtime value into its arguments.Zebrass
@Wei Peng 彭巍 : The macro does get the source at compile time - long before evaluation.Guyenne
I
0

The list you are giving to your macro has the form

(Quote (...))

So the list you actually want is the CADR of the list you get.

Incision answered 17/4, 2013 at 12:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.