From: Peter Seibel
Subject: Is anyone this paranoid?
Date: 
Message-ID: <m365hy6nc6.fsf@javamonkey.com>
Suppose one is writing a (silly) macro DO-PRIMES that is an analog to
DOTIMES except it iterates over successive primes. A fairly normal
implementation might look like this:

  (defmacro do-primes ((var start end) &body body)
    (let ((end-value (gensym)))
      `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
            (,end-value ,end))
           ((> ,var ,end-value))
         ,@body)))

Now, if one was sufficiently paranoid, one might worry that someone
(since they are, after all, out to get you) is going to locally bind
the helper function NEXT-PRIME (which ordinarily returns the next
prime number greater or equal to its argument.) Since the NEXT-PRIME
we want is the global function, if one wanted to give in to one's
paranoia and up the ante in the immovable-object vs. irresistable
force battle, one could write DO-PRIMES like this:

  (defmacro do-primes ((var start end) &body body)
    (let ((end-value (gensym))
          (next-prime (gensym)))
      `(progn
         (setf (symbol-function ',next-prime) (symbol-function 'next-prime))
         (do ((,var (,next-prime ,start) (,next-prime (1+ ,var)))
              (,end-value ,end))
             ((> ,var ,end-value))
           ,@body))))

But is anyone that paranoid? (Obviously one can use packages to make
the first version even safer--either by hinting via not exporting the
NEXT-PRIME symbol or by flat out uninterning it after DO-PRIMES is
defined.)

I assume most folks aren't that paranoid and count on the package
system combined, perhaps, with the relative infrequency of use of FLET
and LABELS to provide the requisite hygine. Is that about right?

(Another paranoid approach would be to move the helper functions into
a FLET or LABELS in the macro expansion. Are Lisp compilers generally
smart enough to somehow share the code for the identical copies of the
local functions that would be created everywhere such a macro is
used?)

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp

From: Thomas F. Burdick
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <xcvptg6ym1z.fsf@famine.OCF.Berkeley.EDU>
Peter Seibel <·····@javamonkey.com> writes:

> Suppose one is writing a (silly) macro DO-PRIMES that is an analog to
> DOTIMES except it iterates over successive primes. A fairly normal
> implementation might look like this:
> 
>   (defmacro do-primes ((var start end) &body body)
>     (let ((end-value (gensym)))
>       `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
>             (,end-value ,end))
>            ((> ,var ,end-value))
>          ,@body)))
> 
> Now, if one was sufficiently paranoid, one might worry that someone
> (since they are, after all, out to get you) is going to locally bind
> the helper function NEXT-PRIME (which ordinarily returns the next
> prime number greater or equal to its argument.) Since the NEXT-PRIME
> we want is the global function, if one wanted to give in to one's
> paranoia and up the ante in the immovable-object vs. irresistable
> force battle, one could write DO-PRIMES like this:

In this case, they're not out to get *you*, they're out to get
themselves.  If you're binding functions in your own package,
presumably you know what you're doing.  If you're using a macro from
package A, and binding functions from the same package, you *better*
know what you're doing.  If you're using a macro from package A, and
binding functions from package B, and package A uses the function from
package B in its expansion ... then you've got something a little
different.  In the last case, I think the author of A:MACRO is at
fault, unless packages A and B come as a unit.

> (Another paranoid approach would be to move the helper functions into
> a FLET or LABELS in the macro expansion. Are Lisp compilers generally
> smart enough to somehow share the code for the identical copies of the
> local functions that would be created everywhere such a macro is
> used?)

I don't think so.  But what you *should* do is:

(in-package :foo)

(defun %frob (&rest args) (apply #'bar:frob args))
(defmacro foo ... (%frob ...) ...)

(in-package :bork)

(flet ((bar:frob ...))
  (foo ...))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Paul F. Dietz
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <RvSdnbO4Bu5UvDei4p2dnA@dls.net>
Peter Seibel wrote:

>  Since the NEXT-PRIME
> we want is the global function, if one wanted to give in to one's
> paranoia and up the ante in the immovable-object vs. irresistable
> force battle, one could write DO-PRIMES like this:
> 
>   (defmacro do-primes ((var start end) &body body)
>     (let ((end-value (gensym))
>           (next-prime (gensym)))
>       `(progn
>          (setf (symbol-function ',next-prime) (symbol-function 'next-prime))
>          (do ((,var (,next-prime ,start) (,next-prime (1+ ,var)))
>               (,end-value ,end))
>              ((> ,var ,end-value))
>            ,@body))))
> 
> But is anyone that paranoid?

This will cause the gensym to stick around, most likely.  Why not:

   (defmacro do-primes ((var start end) &body body)
     (let ((end-value (gensym))
           (np (gensym)))
       `(let ((,np #'next-prime))
	 (declare (type function ,np))
          (do ((,var (funcall ,np ,start) (funcall ,np (1+ ,var)))
               (,end-value ,end))
              ((> ,var ,end-value))
            ,@body))))

This is still vulnerable to next-prime's function binding being changed
outside the loop, but then so was your macro.

I don't think it's usual style to be this paranoid.

	Paul
From: Kent M Pitman
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <wkn0b9yd1c.fsf@nhplace.com>
"Paul F. Dietz" <·····@dls.net> writes:

> Peter Seibel wrote:
> 
> > Since the NEXT-PRIME
> > we want is the global function, if one wanted to give in to one's
> > paranoia and up the ante in the immovable-object vs. irresistable
> > force battle, one could write DO-PRIMES like this: [...]
> > But is anyone that paranoid?
> 
> This will cause the gensym to stick around, most likely.  Why not: [...]
> This is still vulnerable to next-prime's function binding being changed
> outside the loop, but then so was your macro.

> I don't think it's usual style to be this paranoid.

This last sentence is the right answer.

Every function, not just local functions, is "vulnerable" to redefinition,
if you want to use that metaphor.  One cannot program at all if one is
going to be concerned that redefinability is equivalent to vulnerability.
One controls vulnerability by access to the files, by access to the running
process, by not calling EVAL on code that isn't known to be safe, etc. 
One does not control vulnerability by randomly protecting one function 
to excess while leaving others "exposed".  

The package system is the real protection, as Thomas Burdick has already
observed.  (I wouldn't have replied at all to this, since his explanation
was quite clear, except that his reply didn't seem to end the discussion,
and I wanted to add extra weight to it as the definitive answer.)

In sum:  People programming in your own package should be either "just you"
or "people you (a) trust and (b) talk to about conventions that will keep
you from treading on one another".  If you find people can't do that,
don't let them into your package.  People programming in other packages
should not define symbols they are inheriting, so if you've exported 
NEXT-PRIME, they should shadow it if they want to redefine it.  Good 
development environments will flag redefinition of a function in a file
that it's not originally defined in.

The reason that we create the "opportunity" for others to redefine our
functions is to create a flexibility of debugging.  Some people don't buy
into this model and would prefer that debugging be really, really hard.
That's fine.  They can use some other language.  Our language is not for
people who prefer to purchase safety by making day-to-day life 
super-difficult.  Common manners will assure common safety.

Freedom implies both personal responsibility and some degree of risk,
as we are all learning in the real world these days.  The only
alternative, if you want real safety, is to live in a totalitarian
state that takes away all your rights in exchange for your safety.
The only question at that point is "what am I protecting?" if you're
making it safe to "live" with no rights...
From: Lars Brinkhoff
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <857k2dhikb.fsf@junk.nocrew.org>
> Peter Seibel wrote:
> > (setf (symbol-function ',next-prime) (symbol-function 'next-prime))

"Paul F. Dietz" <·····@dls.net> writes:
>   `(let ((,np #'next-prime))

> This is still vulnerable to next-prime's function binding being changed
> outside the loop, but then so was your macro.

But #'next-prime captures local function bindings, whereas
(symbol-function 'next-prime) refers to the global function
definition, right?

If next-prime is defined before the macro, how about

        (let ((fn #'next-prime))
          (defmacro ...
            ...
            `(... (funcall ,fn ...) ...)
            ...))

?

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Frode Vatvedt Fjeld
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <2hsml1l4h1.fsf@vserver.cs.uit.no>
Peter Seibel <·····@javamonkey.com> writes:

> But is anyone that paranoid?

Not if they are in their right mind. But if they aren't, isn't this
an easier way to protect agains shadowing flets or labels?

  (defmacro do-primes ((var start end) &body body)
    (let ((end-value (gensym)))
      `(do ((,var (funcall 'next-prime ,start) (funcall 'next-prime (1+ ,var)))
            (,end-value ,end))
           ((> ,var ,end-value))
         ,@body)))

-- 
Frode Vatvedt Fjeld
From: Pascal Costanza
Subject: Re: Is anyone this paranoid?
Date: 
Message-ID: <boec75$euq$1@newsreader2.netcologne.de>
Peter Seibel wrote:

> Suppose one is writing a (silly) macro DO-PRIMES that is an analog to
> DOTIMES except it iterates over successive primes. A fairly normal
> implementation might look like this:
> 
>   (defmacro do-primes ((var start end) &body body)
>     (let ((end-value (gensym)))
>       `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
>             (,end-value ,end))
>            ((> ,var ,end-value))
>          ,@body)))
> 
> Now, if one was sufficiently paranoid, one might worry that someone
> (since they are, after all, out to get you) is going to locally bind
> the helper function NEXT-PRIME (which ordinarily returns the next
> prime number greater or equal to its argument.) Since the NEXT-PRIME
> we want is the global function, if one wanted to give in to one's
> paranoia and up the ante in the immovable-object vs. irresistable
> force battle, one could write DO-PRIMES like this:
[...]

It's not a bug, it's a feature. (tm)

Just make NEXT-PRIME part of the "interface" of that macro by 
documenting it. This gives someone who wants to use your macro the 
ability to add to the definition of NEXT-PRIME, for example, by 
redefining it with a memoized version, or by looking prime numbers up in 
a precomputed list, just in case one needs to get around a bottle neck, etc.


Pascal