From: Chris Riesbeck
Subject: warn if not top-level?
Date: 
Message-ID: <criesbeck-B9D9F5.14095027062005@individual.net>
I have a macro RUN-TESTS that runs test code. (Tests
are defined with DEFINE-TEST.) To support tests on macros,
test code is stored uncompiled and assembled when RUN-TESTS
is expanded.

I would like to warn users that code like

(DEFUN MY-TESTS () (RUN-TESTS ...))

may be a mistake, since the code saved in MY-TESTS will
no longer be updated when tests are updated or macros
are redefined.

Is there any portable way to recognize the above situation?

Or should I change RUN-TESTS to expand into code that
collects code at run-time and calls EVAL?

From: Kaz Kylheku
Subject: Re: warn if not top-level?
Date: 
Message-ID: <1119905678.345530.178460@g43g2000cwa.googlegroups.com>
Chris Riesbeck wrote:
> I have a macro RUN-TESTS that runs test code. (Tests
> are defined with DEFINE-TEST.) To support tests on macros,
> test code is stored uncompiled and assembled when RUN-TESTS
> is expanded.
>
> I would like to warn users that code like
>
> (DEFUN MY-TESTS () (RUN-TESTS ...))
>
> may be a mistake, since the code saved in MY-TESTS will
> no longer be updated when tests are updated or macros
> are redefined.

Ah, okay, so RUN-TESTS regurgitates the previously stored forms, so
they are subject to a new round of macro-expansion. You want users to
type (RUN-TESTS ...) into a REPL so that this expansion happens over
and over again using the latest versions of any macros that are
involved.

You know, it won't exactly help you to have a test whether a form is
top level. Someone could stick a (RUN-TESTS ...) as the top level form
of a file which is then compiled. The resulting module can then be
reloaded again and again without the macros being reexpanded. Being at
the top level is not the same thing as being repeatedly evaluated.
Being read in a REPL is the same thing as being repeatedly evaluated.

> Is there any portable way to recognize the above situation?
>
> Or should I change RUN-TESTS to expand into code that
> collects code at run-time and calls EVAL?

Note that you don't have to change RUN-TESTS itself to do this; you can
do the EVAL outside, namely:

(eval '(run-tests ...))

I.e. the only code you need to collect to get this run-time expansion
is the source code of the RUN-TESTS expression itself, which is the
root of the macro-expansion tree.
From: Chris Riesbeck
Subject: Re: warn if not top-level?
Date: 
Message-ID: <criesbeck-2D3870.16452527062005@individual.net>
In article <························@g43g2000cwa.googlegroups.com>,
 "Kaz Kylheku" <········@gmail.com> wrote:

> Chris Riesbeck wrote:
> >
> > I would like to warn users that code like
> >
> > (DEFUN MY-TESTS () (RUN-TESTS ...))
> >
> > may be a mistake....
> 
> Ah, okay, so RUN-TESTS regurgitates the previously stored forms, so
> they are subject to a new round of macro-expansion. You want users to
> type (RUN-TESTS ...) into a REPL so that this expansion happens over
> and over again using the latest versions of any macros that are
> involved.

Exactly.

> You know, it won't exactly help you to have a test whether a form is
> top level. Someone could stick a (RUN-TESTS ...) as the top level form
> of a file which is then compiled. The resulting module can then be
> reloaded again and again without the macros being reexpanded. 

Yep. But that's not a common mis-use of RUN-TESTS, whereas
the DEFUN case is.

> > Or should I change RUN-TESTS to expand into code that
> > collects code at run-time and calls EVAL?
> 
> Note that you don't have to change RUN-TESTS itself to do this; you can
> do the EVAL outside, namely:
> 
> (eval '(run-tests ...))
> 
> I.e. the only code you need to collect to get this run-time expansion
> is the source code of the RUN-TESTS expression itself, which is the
> root of the macro-expansion tree.

Yes, but that's not very programmer-friendly. If I go that route,
I'd rather have RUN-TESTS expand into an EVAL.
From: Barry Margolin
Subject: Re: warn if not top-level?
Date: 
Message-ID: <barmar-952AE0.20142827062005@comcast.dca.giganews.com>
In article <·······························@individual.net>,
 Chris Riesbeck <·········@yahoo.com> wrote:

> I have a macro RUN-TESTS that runs test code. (Tests
> are defined with DEFINE-TEST.) To support tests on macros,
> test code is stored uncompiled and assembled when RUN-TESTS
> is expanded.
> 
> I would like to warn users that code like
> 
> (DEFUN MY-TESTS () (RUN-TESTS ...))
> 
> may be a mistake, since the code saved in MY-TESTS will
> no longer be updated when tests are updated or macros
> are redefined.
> 
> Is there any portable way to recognize the above situation?
> 
> Or should I change RUN-TESTS to expand into code that
> collects code at run-time and calls EVAL?

It's not portable, but the only solution I can think of would be to 
redefine the DEFUN macro.

(setf (macro-function 'real-defun) (macro-function 'defun))

(defmacro defun (name (&rest args) &body body)
  `(macrolet ((run-tests (&rest tests)
                (declare (ignore tests))
                (error "RUN-TESTS should only be used at top-level.")))
     (real-defun ,name ,args ,@body)))

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Coby Beck
Subject: Re: warn if not top-level?
Date: 
Message-ID: <wyfwe.63604$wr.56136@clgrps12>
"Chris Riesbeck" <·········@yahoo.com> wrote in message 
····································@individual.net...
>I have a macro RUN-TESTS that runs test code. (Tests
> are defined with DEFINE-TEST.) To support tests on macros,
> test code is stored uncompiled and assembled when RUN-TESTS
> is expanded.
>
> I would like to warn users that code like
>
> (DEFUN MY-TESTS () (RUN-TESTS ...))
>
> may be a mistake, since the code saved in MY-TESTS will
> no longer be updated when tests are updated or macros
> are redefined.
>
> Is there any portable way to recognize the above situation?
>
> Or should I change RUN-TESTS to expand into code that
> collects code at run-time and calls EVAL?

I know "don't do it" is an unsatisfying answer to "how do I do it?" but that 
is probably mine in this case.  The 
"redefined-macro-mean-recompile-all-uses" rule must be well learned, usually 
through pain therapy.  You might do someone a very small favor with all your 
contortions but it won't help them with all the cases where the function 
called by run-tests contains old macro expansions (and may simply delay the 
learning of the important lesson).  Everybody either knows or must soon 
learn that when you redefine a macro, you must recompile everything that 
uses it.

-- 
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
From: Chris Riesbeck
Subject: Re: warn if not top-level?
Date: 
Message-ID: <criesbeck-2C007A.12325228062005@individual.net>
In article <····················@clgrps12>,
 "Coby Beck" <·····@mercury.bc.ca> wrote:

> "Chris Riesbeck" <·········@yahoo.com> wrote in message 
> ····································@individual.net...
> >
> > I would like to warn users that code like
> >
> > (DEFUN MY-TESTS () (RUN-TESTS ...))
> >
> > may be a mistake, since the code saved in MY-TESTS will
> > no longer be updated when tests are updated or macros
> > are redefined.
> 
> I know "don't do it" is an unsatisfying answer to "how do I do it?" but that 
> is probably mine in this case.  The 
> "redefined-macro-mean-recompile-all-uses" rule must be well learned, usually 
> through pain therapy.  You might do someone a very small favor with all your 
> contortions but it won't help them with all the cases where the function 
> called by run-tests contains old macro expansions (and may simply delay the 
> learning of the important lesson).  Everybody either knows or must soon 
> learn that when you redefine a macro, you must recompile everything that 
> uses it.

While this is certainly true for most code, it goes
against the central point of test-driven development.

For now, I've decided to go the run-time evaluation
route. 

Any comparative pros and cons regarding EVAL, COMPILE
and COERCE for turning a LAMBDA form into runnable code?

I'm using COERCE, as possibly simpler than EVAL,
and possibly faster than creating compiled code
than COMPILE, based on some prior discussions I've found.
From: Coby Beck
Subject: Re: warn if not top-level?
Date: 
Message-ID: <CBhwe.64050$wr.2884@clgrps12>
"Chris Riesbeck" <·········@yahoo.com> wrote in message 
····································@individual.net...
> In article <····················@clgrps12>,
> "Coby Beck" <·····@mercury.bc.ca> wrote:
>
>> "Chris Riesbeck" <·········@yahoo.com> wrote in message
>> ····································@individual.net...
>> >
>> > I would like to warn users that code like
>> >
>> > (DEFUN MY-TESTS () (RUN-TESTS ...))
>> >
>> > may be a mistake, since the code saved in MY-TESTS will
>> > no longer be updated when tests are updated or macros
>> > are redefined.
>>
>> I know "don't do it" is an unsatisfying answer to "how do I do it?" but 
>> that
>> is probably mine in this case.  The
>> "redefined-macro-mean-recompile-all-uses" rule must be well learned, 
>> usually
>> through pain therapy.  You might do someone a very small favor with all 
>> your
>> contortions but it won't help them with all the cases where the function
>> called by run-tests contains old macro expansions (and may simply delay 
>> the
>> learning of the important lesson).  Everybody either knows or must soon
>> learn that when you redefine a macro, you must recompile everything that
>> uses it.
>
> While this is certainly true for most code, it goes
> against the central point of test-driven development.

I don't see how?  Shouldn't one have the habit of recompiling before running 
tests?  Also regardless of your methodology, you have to recompile 
macro-using functions after updating the definitions, there is no way to 
sneak around this.

> For now, I've decided to go the run-time evaluation
> route.
>
> Any comparative pros and cons regarding EVAL, COMPILE
> and COERCE for turning a LAMBDA form into runnable code?
>
> I'm using COERCE, as possibly simpler than EVAL,
> and possibly faster than creating compiled code
> than COMPILE, based on some prior discussions I've found.

One thing I like to do in using a test harness is put tests in closures so I 
can use lexical variables.  Expanding into an eval would take away this 
capability.

eg
(let ((test-data (make-test-data...)))
  (loop for arg in test-data
      do (run-test arg)))

That kind of thing.  I think if you expand in to and (eval '(run-test arg)) 
you will have unbound variable problems.


-- 
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")