From: Alain Picard
Subject: Is this bad style?
Date: 
Message-ID: <86d6riyypc.fsf@gondolin.local.net>
Dear Lispers,

Recently, I found myself writing this weird looking macro:


(defmacro each-nth-invocation ((n) &body body)
  "Perform body , but only each Nth invocation."
  (let ((counter (gensym "COUNTER"))
	(current-value (gensym "VALUE")))
    (setf (get counter 'count) 0)
    `(symbol-macrolet ((,current-value (get ',counter 'count)))
      (setf ,current-value (mod (incf ,current-value) ,n))
      (when (zerop ,current-value)
	,@body))))

This allows me to embed form in infinite loops like

(loop
   (wait-for-some-condition-to-happen)
   (do-some-stuff)
   (each-nth-invocation (100)
      (do-expensive-catchup-stuff)))

where the expensive stuff only happens 100 times less often
than the normal stuff.  Or do things like

(defun surprise ()
  (do-normal-stuff)
  (each-nth-invocation (once-per-blue-moon) (surprise-them!)))

And calling surprise surprises you once in a blue moon.

My colleague and I discussed this, and had never seen this sort
of macro before.  This normally rings a bell with me that there's
some really good reason why this is _BAD_, and that I'm missing it.

What do people think of this sort of macro?  I'm now deeply curious.

From: Frode Vatvedt Fjeld
Subject: Re: Is this bad style?
Date: 
Message-ID: <2hk7lqgn8r.fsf@vserver.cs.uit.no>
Alain Picard <·······················@optushome.com.au> writes:

> (defmacro each-nth-invocation ((n) &body body)
>   "Perform body , but only each Nth invocation."
>   (let ((counter (gensym "COUNTER"))
> 	(current-value (gensym "VALUE")))
>     (setf (get counter 'count) 0)
>     `(symbol-macrolet ((,current-value (get ',counter 'count)))
>       (setf ,current-value (mod (incf ,current-value) ,n))
>       (when (zerop ,current-value)
> 	,@body))))

I think this is bad style because the expander function is used to
initialize the environment. I believe macro-expanders as far as
possible should be pure code-transforming functions. Also, I don't
quite understand what the symbol-macro is there for. Both issues are
easily fixed, though:

  (defmacro each-nth-invocation ((n) &body body)
   "Perform body, but only every nth invocation."
   (check-type n (integer 1 *) "an integer greater than one")
   (let ((counter (gensym "COUNTER-")))
     `(when (zerop (mod (incf (get ',counter 'count 0)) ,n))
 	,@body)))

This is using a symbol property as a global variable. I think this is
okay, but you could alternatively use a special variable. Actually I'd
be interested to hear other people's opinions on what is the better
choice in this case.

-- 
Frode Vatvedt Fjeld
From: Frode Vatvedt Fjeld
Subject: Re: Is this bad style?
Date: 
Message-ID: <2hfzwegkvy.fsf@vserver.cs.uit.no>
Frode Vatvedt Fjeld <······@acm.org> writes:

>  (defmacro each-nth-invocation ((n) &body body)
>    "Perform body, but only every nth invocation."
>    (check-type n (integer 1 *) "an integer greater than one")
>    (let ((counter (gensym "COUNTER-")))
>      `(when (zerop (mod (incf (get ',counter 'count 0)) ,n))
>  	,@body)))

No, wait, this is still not okay since effectively it is here the
expander which is creating the global variable. Presumably this will
behave very badly in interpreted evaluation.

I dawns on me that the Right Thing here must be such that the body is
associated with some recognizable identifier, in order for the counter
not to be reset at random times such as compilation or interpreted
evaluation.

  (defmacro each-nth-invocation ((tag n) &body body)
    "Perform body, but only every nth invocation."
    (check-type n (integer 1 *) "a positive integer")
    `(when (zerop (mod (incf (get ',var 'count 0)) ,n))
       ,@body)))

In this case, however, I lean even more towards using special
variables, although I'm not quite certain if this is appropriate use
of defvar:

  (defmacro each-nth-invocation ((tag n) &body body)
    "Perform body, but only every nth invocation."
    (check-type n (integer 1 *) "a positive integer")
    `(progn
       (defvar ,var 0)
       (when (zerop (mod (incf ,var) ,n))
         ,@body))


Note also that now two each-nth-invocation forms can be associated
with the same counter, which might sometimes be useful.

-- 
Frode Vatvedt Fjeld
From: Paul Foley
Subject: Re: Is this bad style?
Date: 
Message-ID: <m2u1ku84h6.fsf@mycroft.actrix.gen.nz>
On Fri, 13 Sep 2002 13:09:08 +0200, Frode Vatvedt Fjeld wrote:

>   (defmacro each-nth-invocation ((n) &body body)
>    "Perform body, but only every nth invocation."
>    (check-type n (integer 1 *) "an integer greater than one")
>    (let ((counter (gensym "COUNTER-")))
>      `(when (zerop (mod (incf (get ',counter 'count 0)) ,n))
>  	,@body)))

> This is using a symbol property as a global variable. I think this is
> okay, but you could alternatively use a special variable. Actually I'd
> be interested to hear other people's opinions on what is the better
> choice in this case.

  `(let ((,counter (load-time-value (list 0))))
      ...) ; using (car ,counter) for the value

-- 
If that makes any sense to you, you have a big problem.
                                      -- C. Durance, Computer Science 234
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Tim Bradshaw
Subject: Re: Is this bad style?
Date: 
Message-ID: <ey3sn0enkmy.fsf@cley.com>
* Alain Picard wrote:

> (defmacro each-nth-invocation ((n) &body body)
>   "Perform body , but only each Nth invocation."
>   (let ((counter (gensym "COUNTER"))
> 	(current-value (gensym "VALUE")))
>     (setf (get counter 'count) 0)
>     `(symbol-macrolet ((,current-value (get ',counter 'count)))
>       (setf ,current-value (mod (incf ,current-value) ,n))
>       (when (zerop ,current-value)
> 	,@body))))

I think this is nicer as it doesn't use any global state.

    (defmacro every-nth (n &body code)
      (let ((cc (make-symbol "C"))
            (nn (make-symbol "N")))
        `(let ((,cc (load-time-value (list 0) nil))
               (,nn ,n))
           (when (zerop (setf (car ,cc) (mod (1+ (car ,cc)) ,nn)))
             ,@code))))

There's obviously a question of whether every nth means the first time
and then the n+1st time or the nth time and then the 2nth and so on...

--tim
From: Alain Picard
Subject: Re: Is this bad style?
Date: 
Message-ID: <8665xaysay.fsf@gondolin.local.net>
Tim Bradshaw <···@cley.com> writes:

> * Alain Picard wrote:
> 
> I think this is nicer as it doesn't use any global state.
> 
>     (defmacro every-nth (n &body code)
>       (let ((cc (make-symbol "C"))
>             (nn (make-symbol "N")))
>         `(let ((,cc (load-time-value (list 0) nil))
>                (,nn ,n))
>            (when (zerop (setf (car ,cc) (mod (1+ (car ,cc)) ,nn)))
>              ,@code))))
> 

I don't understand your use of MAKE-SYMBOL here, instead of GENSYM.
Is that because it's better to just use the two uninterned
symbolx #:C and #:N to hold the state for every macroexpansion,
rather than polluting the image with extra gensyms on each macroexpansion?

Also, both you and Paul Foley have mentioned LOAD-TIME-VALUE; I'm
reading up about this form and haven't yet figured out that its
use here is indicated and desirable.  Thanks, Tim, I shall study
the above macro very carefully.

Lastly, thanks to all who commented on the _implementation_ of the 
macro, but I'm still interested in comments on the desirability/usefulness
of such a construct.
From: Tim Bradshaw
Subject: Re: Is this bad style?
Date: 
Message-ID: <ey3n0qmnirf.fsf@cley.com>
* Alain Picard wrote:
> I don't understand your use of MAKE-SYMBOL here, instead of GENSYM.
> Is that because it's better to just use the two uninterned symbolx
> #:C and #:N to hold the state for every macroexpansion, rather than
> polluting the image with extra gensyms on each macroexpansion?

I tend to use MAKE-SYMBOL rather than GENSYM.  I don't think it really
matters - they both produce a unique symbol which is what you want,
but somehow MAKE-SYMBOL is easier.

> Lastly, thanks to all who commented on the _implementation_ of the
> macro, but I'm still interested in comments on the
> desirability/usefulness of such a construct.

I think it's reasonably useful sometimes.  Especially if you want to
instrument some code without adding new loop variables and so on.  For
instance to print something every nth time a function is called
from a loop from within that function (where there is no access to the
loop variables anyway).

--tim
From: Glenn Burnside
Subject: Re: Is this bad style?
Date: 
Message-ID: <uo3sdq9pokc3f@corp.supernews.com>
"Tim Bradshaw" <···@cley.com> wrote in message
····················@cley.com...
> * Alain Picard wrote:
> > I don't understand your use of MAKE-SYMBOL here, instead of GENSYM.
> > Is that because it's better to just use the two uninterned symbolx
> > #:C and #:N to hold the state for every macroexpansion, rather than
> > polluting the image with extra gensyms on each macroexpansion?
>
> I tend to use MAKE-SYMBOL rather than GENSYM.  I don't think it really
> matters - they both produce a unique symbol which is what you want,
> but somehow MAKE-SYMBOL is easier.

But make-symbol interns the symbol in the current package, and gensym
doesn't, right?
From: Tim Bradshaw
Subject: Re: Is this bad style?
Date: 
Message-ID: <ey3hegunfc2.fsf@cley.com>
* Glenn Burnside wrote:

> But make-symbol interns the symbol in the current package, and gensym
> doesn't, right?

No.  MAKE-SYMBOL just makes a new symbol, it doesn't intern it in any
package:

    make-symbol creates and returns a fresh, uninterned symbol whose
    name is the given name. The new-symbol is neither bound nor fbound
    and has a null property list.

--tim
From: Bruce Hoult
Subject: Re: Is this bad style?
Date: 
Message-ID: <bruce-FFBA04.23390413092002@copper.ipg.tsnz.net>
In article <··············@gondolin.local.net>,
 Alain Picard <·······················@optushome.com.au> wrote:

> My colleague and I discussed this, and had never seen this sort
> of macro before.  This normally rings a bell with me that there's
> some really good reason why this is _BAD_, and that I'm missing it.
> 
> What do people think of this sort of macro?  I'm now deeply curious.

For what it's worth, I've been doing macros just like this (or inline 
functions, where supported) in C and C++ for fifteen years -- when 
you're doing CPU-intensive things in traditional Mac programs, you need 
to call waitNextEvent often enough to let other programs get time, but 
not so often that the system spends *all* its time task switching.  
Hence a macro that you can sprinkle everywhere that is usually very very 
cheap but sometimes does a task switch.

-- Bruce