11

In my quest to fully understand the so powerful lisp macros a question came to my mind. I know that a golden rule about macros is the one saying "Never use a macro when a function will do the work". However reading Chapter 9 - Practical: Building a Unit Test Framework - from the book Practical Common Lisp I was introduced to the below macro whose purpose was to get rid of the duplication of the test case expression, with its attendant risk of mislabeling of results.

;; Function defintion. 

(defun report-result (result form)
  (format t "~:[FAIL~;pass~] ... ~a~%" result form))

;; Macro Definition

(defmacro check (form)
  `(report-result ,form ',form))

OK, I understand its purpose but I could have done it using a function instead of a macro, for instance:

(setf unevaluated.form '(= 2 (+ 2 3)))

(defun my-func (unevaluated.form)
  (report-result (eval unevaluated.form) unevaluated.form))
  1. Is this only possible because the given macro is too simple ?
  2. Furthermore, is Lisp Macro System so powerful relatively its opponents due to the code itself - like control structures, functions, etc - is represented as a LIST ?
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
utxeee
  • 953
  • 1
  • 12
  • 24
  • 6
    This "golden rule" is stupid. Use macros wherever you find them appropriate and forget about all the brain-damaged "rules". As for your example, it is not nearly an equivalent, since you're deferring a compilation to runtime. If your form contains references to some locally scoped names it simply will not work. – SK-logic Jul 21 '12 at 23:38
  • Sk, could you give an example regarding the eval function and locally scoped name ? – utxeee Jul 22 '12 at 09:20
  • 3
    `(let ((x 2)) (eval '(+ x x)))` simply would not work, OTOH if this `(+ x x)` form was generated by a macro it would be naturally compiled. – SK-logic Jul 23 '12 at 08:09
  • This qst comes up first on a google search yet it doesn't really explain the difference between a Lisp macro and function. Seems the question subject line could have been better written. JMO. –  Dec 11 '13 at 14:19

3 Answers3

14

But if it were a macro you, could have done:

(check (= 2 (+ 2 3)))

With a function, you have to do:

(check '(= 2 (+ 2 3)))

Also, with the macro the (= 2 (+ 2 3)) is actually compiled by the compiler, whereas with the function it's evaluated by the eval function, not necessarily the same thing.

Addenda:

Yes, it's just evaluating the function. Now what that means is dependent upon the implementation. Some can interpret it, others can compile and execute it. But the simple matter is that you don't know from system to system.

The null lexical environment that others are mentioning is also a big deal.

Consider:

(defun add3f (form)
  (eval `(+ 3 ,form)))

(demacro add3m (form)
  `(+ 3 ,form))

Then observe:

[28]> (add3m (+ 2 3))
8
[29]> (add3f '(+ 2 3))
8
[30]> (let ((x 2)) (add3m (+ x 3)))
8
[31]> (let ((x 2)) (add3f '(+ x 3)))

*** - EVAL: variable X has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of X.
STORE-VALUE    :R2      Input a new value for X.
ABORT          :R3      Abort main loop
Break 1 [32]> :a

That's really quite damning for most use cases. Since the eval has no lexical environment, it can not "see" the x from the enclosing let.

Will Hartung
  • 115,893
  • 19
  • 128
  • 203
4

The better substitution would be not with eval, which won't perform as expected for all cases (for example, it doesn't have access to the lexical environment), and is also overkill (see here: https://stackoverflow.com/a/2571549/977052), but something using anonymous functions, like this:

(defun check (fn)
  (report-result (funcall fn) (function-body fn)))

CL-USER> (check (lambda () (= 2 (+ 2 3))))

By the way, this is how such things are accomplished in Ruby (anonymous functions are called procs there).

But, as you see, it becomes somewhat less elegant (unless you add syntax sugar) and, there's actually a bigger problem: ther's no function-body function in Lisp (although there may be non-standard ways to get at it). Overall, as you see, for this particular task the alternative solutions are substantially worse, although in some cases such approach could work.

In general, though, if you want to do something with the source code of the expressions passed into the macro (and usually this is the primary reason of using macros), functions would not be sufficient.

Community
  • 1
  • 1
Vsevolod Dyomkin
  • 9,343
  • 2
  • 31
  • 36
3

The report-result function needs both the source code and the result of the execution.

The macro CHECK provides both from a single source form.

If you put a bunch of check forms into the file, they are easily compiled using the usual process of compiling Lisp files. You'll get a compiled version of the checking code.

Using a function and EVAL (better use COMPILE) you would have deferred the source evaluation to a later time. It would also not be clear if it is interpreted or compiled. In case of compilation, you would then later get the compiler's checks.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346