From: Kent M Pitman
Subject: Re: evil macro problem
Date: 
Message-ID: <sfw66r5po8i.fsf@world.std.com>
kp gores <·····@sip.medizin.uni-ulm.de> writes:

> can someone give me advice on how to track down where exactely the 
> compiler hangs, eg. with options to compile-file?

Age old wisdom: Try adding some print statements and/or using TRACE?

Sometimes if you just see the macro calls getting applied, you'll be
surprised by what your macro gets called on.  For example, could one
of semantics or validity be also a macro that expands into (id->idref
,structured-text)?  If so, you'd have an infinite loop...  This can
happen even where a DEFUN would be harmless if the "recursion" was
guarded by something (an IF or COND) to keep infinite recursion from
happening at runtime but the semantic analysis (macro expansion) is
ignoring the guard.

> is it possible to write recursive macros?

It is possible to have macros that expand to references to themselves.
You have to be careful that they terminate without appeal to information
about the eventual runtime situation.

> if not, would the compiler 
> detect that?

It's doubtful the compiler would know or care that you had macros expanding
into references to the same macro, except perhaps in some cases to do you
the favor of detecting loops.

p.s. Macros are, of course, not evil.

From: Drew McDermott
Subject: Re: evil macro problem
Date: 
Message-ID: <39511D3D.96257952@yale.edu>
Kent M Pitman wrote:

      p.s. Macros are, of course, not evil.

I agree.  However, I think the one proposed by the original poster is
evil, especially if it's typical of his programming style.  I can
easily see how using lots of macros like this could cause infinite
recursions.  (As Kent pointed out, if ppml, semantics, or validity are
macros, you have to be sure they don't use id->idref.)

However, there is a far worse problem, which is the repetition of
",structured-text" all over the place.  Whatever form structured-text
is bound to, it will be copied six times by this macro!  If it has
side effects, the code will probably be wrong.

But even if it doesn't, the code will be bloated.  If this style is
used a lot, the bloat is not a factor of 6, but a factor of 6 to the
nth power, where n is the depth of nesting and each level repeats its
argument 6 times.  Example: The form (id->idref (big (bad (barfish
x)))) will expand by a factor of 6^4, assuming big, bad, and barfish
are macros, each of which repeats its argument 6 times.

Even simpler example: (defmacro oops (x) `(list ,x ,x ,x ,x ,x ,x))

Then (oops (oops (oops (oops (fact 100))))) expands to

(list (list (list (list (fact 100) (fact 100) (fact 100) (fact 100) (fact 100)
(fact 100))
                   (list (fact 100) (fact 100) (fact 100) (fact 100) (fact 100)
(fact 100))
                   ...
             )))

Of course, we can't avoid the 6^4 conses in this case, but we should
compute the factorial only once, not 6^4 times.

The poster said he wanted to get call by reference and faster code.
As far as I can see, id->idref doesn't do any call by reference.  As
far as speed is concerned, my guess is that the difference in speed is
never significant, except when, as above, it is significantly horrible.

In any case, if you really want this code inlined (and I wouldn't
bother), the right way to do it is to define it as an ordinary
function, but write

(declaim (inline id->idref))

first.  Not all compilers will actually inline it, but the compiler
writers probably understand the efficiency tradeoffs better than you
(or I) do.

By the way, even if you do want call by reference, you should try
defining some standard tools so that you use a small fixed number of
macros rather than tons and tons of them.  E.g., define an abstract
"reference" object produced by, e.g.,
    (let ((r (ref-to (car x)))) ...)

so that (value-of r) is the same as (car x), and (setf (value-of r)
new) is the same as (setf (car x) new), except that the code
manipulating it doesn't know it's the car.

   -- Drew McDermott
From: Pierre R. Mai
Subject: Re: evil macro problem
Date: 
Message-ID: <87r99o1xu3.fsf@orion.dent.isdn.cs.tu-berlin.de>
kp gores <·····@sip.medizin.uni-ulm.de> writes:

> probably i really donT understand what i am doing when i write the macro 
> his way:
> 
> (defmacro ID->IDREF (structured-text)
>    "if there is a ID return a copy of structured-text with ID changed to 
> IDREF, otherwise return structured-text"
> 
>    `(cond
>       ((id ,structured-text)
>        (values  (make-instance 'structuredtext
>                       :ppml (ppml ,structured-text)
>                       :semantics (semantics ,structured-text)
>                       :value (value ,structured-text)
>                       :validity (validity ,structured-text)
>                       :content ""
>                       :contenttype 'atom
>                       :id nil
>                       :idref (id ,structured-text))))
>       (T (values ,structured-text))))
> 
> what i want is to get the above code inlined so it is executed faster.

Note that function call overhead is only very rarely performance
relevant on todays machines and implementations.  Other factors (like
cache hit rate) which are not as directly influencable are often much
more important.  Worrying about function call overhead is only
indicated on functions that are both small, don't do much work by
themselves and have a very high call frequency.

Given that in the above function you will quite often create a new
instance (I presume), the work done by the function will outweigh the
function call overhead.  If the instance creation case is only called
very infrequently, and inlining really is important, you might want to
write this as two functions, one which creates the copy with id
changed to idref, which is not inlined, and one that does the test
which is inlined:

(defun copy-structuredtext-with-idref (structured-text)
  (make-instance 'structuredtext
                 :ppml (ppml structured-text)
                 :semantics (semantics structured-text)
                 :value (value structured-text)
                 :validity (validity structured-text)
                 :content ""
                 :contenttype 'atom
                 :id nil
                 :idref (id structured-text)))

(declaim (inline id->idref))
(defun id->idref (structured-text)
  (if (id structured-text)
      (copy-structuredtext-with-idref structured-text)
      structured-text))

And if your given implementation ignores inline declarations for
user-defined functions (like ACL does), you might want to write a
compiler-macro in adition, which does the inlining by hand:

(define-compiler-macro id->idref (structured-text)
  (let ((st-var (gensym))) ; Not really necessary here, just for completeness
    `(let ((,st-var ,structured-text))
       (if (id ,st-var)
           (copy-structuredtext-with-idref ,st-var)
           ,st-var))))

Note though that I don't recommend this approach (or most other cases
of inlining), until you have determined by profiling that the function
call overhead is really a problem (which again in most cases it isn't).

> i know little about inline functions and nothing about the difference 
> between macros and inline functions, so i use macros.
> i didnt use a function because structured-text could be a very large 
> instance of my class structuredtext and passing it to the function would 
> produce a lot of overhead (i think it would..).

No, it wouldn't.  All non-immediate Lisp objects are passed by
reference in CL, so therefore the size of Lisp objects has no
influence on function call overhead.  Nothing is copied for the
function call, if that was your fear.

> the calls to ppml, semantics etc are all accessors to slots of the class 
> structuredtext. this should be no problem?! 

The problem lies in the fact that you don't know what form
structuredtext will be bound to.  If that form has any side effects,
evaluating it multiple times like you do will lead to problems:

(defvar *structured-text-list* (list ......))

(id->idref (pop *structured-text-list*))

will expand to

(cond
  ((id (pop *structured-text-list*))
   (values (make-instance 'structuredtext
                          :ppml (ppml (pop *structured-text-list*))
                          :semantics (semantics (pop *structured-text-list*))
                          :value (value (pop *structured-text-list*))
                          :validity (validity (pop *structured-text-list*))
                          :content ""
                          :contenttype 'atom
                          :id nil
                          :idref (id (pop *structured-text-list*)))))
   (T (values (pop *structured-text-list*))))

This will not only pop the list 2-6 times instead of once, as was
intended, it will also lead to wrong copies or structuredtexts to be
produced or returned, since the value of the pop form will of course
vary over time...

Regs, Pierre.

-- 
Pierre Mai <····@acm.org>         PGP and GPG keys at your nearest Keyserver
  "One smaller motivation which, in part, stems from altruism is Microsoft-
   bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]
From: Drew McDermott
Subject: Re: evil macro problem
Date: 
Message-ID: <395366D7.603AD966@yale.edu>
kp gores wrote:

what i want is to get the above code inlined so it is executed faster.

> i know little about inline functions and nothing about the difference
> between macros and inline functions, so i use macros.
> i didnt use a function because structured-text could be a very large
> instance of my class structuredtext and passing it to the function would
> produce a lot of overhead (i think it would..).

No, passing an argument in Lisp always takes the same amount of time,
regardless of its size.  That's because behind the scenes Lisp represents
every object by a pointer to the object.  When the argument is passed, all
that has to be copied is that pointer (usually 4 to 8 bytes).  [There may be
some exceptions, where an optimizing compiler can pass an integer rather than
a pointer to the integer, but they don't affect the point I'm making.]

So my guess is you should flush all your macros and replace them with defuns.

    -- Drew McDermott