Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluate the arguments of a macro form

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))))))
like image 358
Wei Peng 彭巍 Avatar asked Mar 27 '13 21:03

Wei Peng 彭巍


1 Answers

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.

like image 186
Rainer Joswig Avatar answered Oct 04 '22 22:10

Rainer Joswig