Lisp: Macros vs Functions [duplicate]
Asked Answered
B

3

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 ?
Berwickupontweed answered 21/7, 2012 at 20:11 Comment(4)
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.Geometrid
Sk, could you give an example regarding the eval function and locally scoped name ?Berwickupontweed
(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.Geometrid
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.Lexicographer
C
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.

Chokedamp answered 21/7, 2012 at 20:28 Comment(3)
Will, so using here eval function I'm just evaluating the expression ?Berwickupontweed
@uxtee: By default, eval operates in the null lexical environment. That is almost never what you want.Champaign
Should say defmacro instead of demacroEmphasize
A
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://mcmap.net/q/21102/-why-exactly-is-eval-evil), 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.

Abstention answered 21/7, 2012 at 20:28 Comment(0)
F
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.

Freeman answered 22/7, 2012 at 2:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.