[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is it possible for a macro to expand to nothing?

From: Pascal J. Bourguignon
Subject: Re: Is it possible for a macro to expand to nothing?
Date: Tue, 24 Nov 2009 12:14:50 +0100
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.3 (darwin)

Alan Mackenzie <address@hidden> writes:

> Pascal J. Bourguignon <address@hidden> wrote:
>> Alan Mackenzie <address@hidden> writes:
>>> Pascal J. Bourguignon <address@hidden> wrote:
>>>> "Drew Adams" <address@hidden> writes:
>>>> This is the problem!  Macros shouldn't return a _list_, they should
>>>> return a _form_.  If you write a macro that returns a list, or you
>>>> use it so that it returns a list, that is not a valid form, then it
>>>> is not good style, even if you catch up.
>>> Is that right?  I think you should be required to justify this
>>> assertion of "good style".  If that "good style" really is good style,
>>> then the whole of cc-langs.el (which uses intense macro magic to
>>> generate data structures with both compile-time and run-time
>>> behaviour) is attrocious style.
>> If that was the case, yes, I would think so.  Macros are designed to
>> generate code, not other data.
> I'm no lisp guru, but I must disagree strongly with this.  What is code,
> what is data?  Surely they are essentiallty the same, particularly in
> lisp.  You would hold that a macro which generates a font-lock-defaults
> structure (so as to reduce the tedium of doing it by hand) is an abuse of
> the macro idea, would you?

There is an essential difference between "code" and "data".

I won't define data, let's take it as axiom.

Code is a kind of data, for which there exist a processor.  Of course,
you can always produce a processor to handle any kind of data:

  (defun data-processor (data)

Lisp code is the kind of data that is accepted by eval.

Valid lisp code is the kind of lisp code that eval can process and
return the result without signaling an error (it can signal errors
internally, if they are caught).

Valid Zorblug code is Zorblug code that zorbluyg can process and
return the result without signaling an error.

(defun zorblug (form)
   (if (listp form)
     (apply (function +) (mapcar (function zorblug) form))
     (error "Not a zorblug form %S" form)))

(zorblug 42)
--> Debugger entered--Lisp error: (error "Not a zorblug form 42")

(zorblug '(nil (nil nil) nil))
--> 0

        data    valid Zorblug code      Lisp code        Valid lisp code
42      yes            no                 yes                 yes
(nil)   yes           yes                 yes                  no
(+ 1 2) yes            no                 yes                 yes
(/ 1 0) yes            no                 yes                  no

My claim is that it is not a good style to write a macro that produces
non valid lisp code, because lisp macros are designed to produce code
that will be evaluated by the lisp eval, not by the zorblug processor
or any other random data processor. (There may be an exception, where
a macro generate lisp code that signal an error, if this is the
specific purpose of the macro; but for a normal macro, it is better if
it generates only valid lisp code).

If you want to transform code for other processors, or random data,
use functions.

Notice that the above data-processor doesn't use lisp macros.  You
could try to hook lisp macros in the data-processor, but this would be
bad style, and it would probably be easier and safer (namespace wise)
to just write your own data macro system, since implementing one is
rather trivial.

>> If you are generating general data, then using functions will be easier
>> and clearer.
> If it's possible.  But if this were the case, using functions to generate
> "code" would be easier and clearer, too.

Absolutely.  And more useful in some circumstances too.  That's why
you should write non trivial macros as a mere call to the code
generating function:

(defmacro non-trivial-macro (arguments...)
   (generate-non-trivial-code arguments...))

>> But cc-langs.el only defines four macros and they all generate
>> perfectly good lisp code.
> Any macro, once debugged, generates "perfectly good" lisp code.  I don't
> understand where this notion of "perfectly good" comes from.

It means valid lisp code, see definition above.

>>> Fact is, though, it allows a simple tabular writing of constants which
>>> vary between C, C++, java, ....  Kudos to Martin Stjernholm, who wrote
>>> it.
>> Unfortunately, most of emacs lisp code is bad code.  Functions one
>> kilometer long, chained with one or more others one kilometer long.
>> Copy-and-pasted chunks instead of abstracting it away.  Etc.
> I can't disagree with that, sadly.  However I think Emacs's code base is
> better than a typical 25 yo application still under development (if there
> is such a beast).

Yes, the fact that it still runs, is still useful, and can still be
maintained despite these bad parts is proof of it, that it's better
than typical 25 yo or even a lot of much younger programs.

>> Now of course, I had a peek at code that had bugs or missing features
>> in the first place.  Perhaps the good quality emacs lisp code I hadn't
>> a peek at because it worked well enough so I didn't need to.
> Perhaps.
>>>> Because it is a better style.  It avoids abusing the ifdef macro.
>>> Where does this notion of "abuse" come from?  What is its rationale?
>>> (This is a genuine question.)
>> The general contract of a macro is that it returns valid forms.
> Sorry, Pascal, you're just restating the same thing again, not answering
> my question.  Why should I accept this "general contract of a macro"?  I
> haven't signed it.  ;-)  Is there some respected Lisp guru who says this?

It comes directly from the definition of defmacro,

    defmacro is a special form in `src/eval.c'.
    (defmacro name arglist [docstring] [decl] body...)

    Define name as a macro.
    The actual definition looks like
     (macro lambda arglist [docstring] [decl] body...).
    When the macro is called, as in (name ARGS...),
    the function (lambda arglist body...) is applied to
    the list ARGS... as it appears in the expression,
    and the result should be a form to be evaluated instead of the original.

(for the definition of form, see the Common Lisp glossary entry I
quoted elsewhere in this thread).

> What would this guru say about the macro which generates a
> font-lock-defaults structure?

If this structure cannot be passed to eval without signaling an error,
then I would say that it is bad style to use a macro to generate such

>> In all fairness, ifdef does return valid forms, when provided valid
>> forms as argument.
>> (defmacro ifdef (expr &rest body)
>>   (and (eval expr) `(progn ,@body)))
> That version of ifdef is ugly because it contains an obtrusive `progn'.

In a way here, progn is the dual of list.

When you want to generate a list of data, you use list:

  `(list  ,expr1 ,expr2 ... ,exprN)

When you want to generate a list of code, you use progn:

  `(progn ,form1 ,form2 ... ,formN)

It is not obtrusive in any way, it only shows that we are generating
code, not mere data.

> The version I used doesn't.  There is no guarantee that a lisp compiler,
> particularly the Emacs lisp byte compiler, is going to optimise away this
> unnecessary artifact.  

All lisp compilers will obviously optimize out embedded progn forms,
because no object code can correspond to it.

(disassemble (byte-compile '(lambda () (progn (progn (progn) (progn)) 
byte code:
  args: nil
0       constant  nil
1       return    

They would optimize out similarly unneeded forms:
(disassemble (byte-compile '(lambda () nil nil nil nil nil)))
produces the same byte code as above.

> It seems this `progn' is there purely to satisfy
> the (as yet unsubstantiated) injunction to return only "perfectly good"
> lisp forms.

Not 'purely'.  You could optimize it out when unnecessary with:

  (defun progn-encapsulate (forms)
    (if (= 1 (length forms)) 
       (first forms)
       (list* 'progn forms)))

  (defmacro ifdef (expr &rest body)
    (and (eval expr) (progn-encapsulate body)))

>> The fact that such a macro call embedded in another form building form
>> that processes it properly doesn't mean that it is not bad style: it
>> has to do something special to the result of ifdef to make it work.
>> If you extract that ifdef call to run it at the repl, it just cannot
>> work.
> Yes.

__Pascal Bourguignon__

reply via email to

[Prev in Thread] Current Thread [Next in Thread]