From: Alain Picard
Subject: How to test a macro?
Date: 
Message-ID: <868zkvol7h.fsf@gondolin.local.net>
Dear lispers,

I am having trouble figuring out how to get an extra
level of evaluation out of a macro call.  Assume that
I have a macro that mimics defclass:

(defmacro my-defclass (class-name supers slots . options)
  `(defclass ,class-name ,supers ,slots ,@options))

(My macro definition does much more than this; however,
this simple definition will illustrate my problem.)

I want to write a test for my-defclass where the name
of the class is a gensym'd symbol.  E.g.,

(deftest test-my-defclass ()
  (let ((class-name (gensym "CLASS")))
    (my-defclass class-name ...)))

The problem should be obvious: the first arg (all args,
actually) to my-defclass is not evaluated and I can't
get to the gensym'd class name that I want to use.  I've
explored using macrolet and symbol-macrolet; but, so far,
I have been unsuccessful in accomplishing what I'm trying
to do.  There has to be some way of doing this ... we are
talking about lisp here, afterall :-).  Can anyone help?

The second problem is indirectly related to the first.
As I was exploring the use of macrolet, I wanted to call
macroexpand-1 on the macrolet macro.  This of course does
not work because the macrolet macro is not defined in the
global environment, wherein macroexpand-1 work.  Yet,
macroexpand-1 does take an env optional argument.  However,
I could find no way of accessing that environment!  My
guess is that *macroexpand-hook* is somehow involved; but
again, I couldn't figure out how to do this.  Help would
be appreciated.

thanks much.


-- 
It would be difficult to construe        Larry Wall, in  article
this as a feature.			 <·····················@netlabs.com>

From: Barry Margolin
Subject: Re: How to test a macro?
Date: 
Message-ID: <a_XD6.29$%S2.1169@burlma1-snr2>
In article <··············@gondolin.local.net>,
Alain Picard  <·······@optushome.com.au> wrote:
>The second problem is indirectly related to the first.
>As I was exploring the use of macrolet, I wanted to call
>macroexpand-1 on the macrolet macro.  This of course does
>not work because the macrolet macro is not defined in the
>global environment, wherein macroexpand-1 work.  Yet,
>macroexpand-1 does take an env optional argument.  However,
>I could find no way of accessing that environment!  My
>guess is that *macroexpand-hook* is somehow involved; but
>again, I couldn't figure out how to do this.  Help would
>be appreciated.

The only way to get an environment object is by defining a macro that has
an &ENVIRONMENT argument.  Within the definition of the macro it can pass
the environment to MACROEXPAND-1.  If you're calling MACROEXPAND-1 manually
from the top-level this optional argument is practically useless.

An example of a macro that makes use of &ENVIRONMENT is SETF.  It needs to
call MACROEXPAND on its first subform, and it has to be in the proper
environment so that something like this will work properly:

(macrolet ((my-car (x) `(car ,x)))
  (setf (my-car some-cons) value))

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Marc Battyani
Subject: Re: How to test a macro?
Date: 
Message-ID: <9bpd1t$f7l$1@reader1.fr.uu.net>
"Alain Picard" <·······@optushome.com.au> wrote

> Dear lispers,
>
> I am having trouble figuring out how to get an extra
> level of evaluation out of a macro call.  Assume that
> I have a macro that mimics defclass:
>
> (defmacro my-defclass (class-name supers slots . options)
>   `(defclass ,class-name ,supers ,slots ,@options))
>
> (My macro definition does much more than this; however,
> this simple definition will illustrate my problem.)
>
> I want to write a test for my-defclass where the name
> of the class is a gensym'd symbol.  E.g.,
>
> (deftest test-my-defclass ()
>   (let ((class-name (gensym "CLASS")))
>     (my-defclass class-name ...)))
>
> The problem should be obvious: the first arg (all args,
> actually) to my-defclass is not evaluated and I can't
> get to the gensym'd class name that I want to use.  I've
> explored using macrolet and symbol-macrolet; but, so far,
> I have been unsuccessful in accomplishing what I'm trying
> to do.  There has to be some way of doing this ... we are
> talking about lisp here, afterall :-).  Can anyone help?

If I'm not missing the point you can use EVAL

(defun test-my-defclass ()
    (let ((class-name (gensym "CLASS")))
           (eval `(my-defclass ,class-name () ()))))
test-my-defclass

INTERFACE 55 > (test-my-defclass)
#<standard-class #:class5446 20400EEC>

Marc
From: David Bakhash
Subject: Re: How to test a macro?
Date: 
Message-ID: <m3lmowgeq1.fsf@alum.mit.edu>
>>>>> "ap" == Alain Picard <·······@optushome.com.au> writes:

 ap> I am having trouble figuring out how to get an extra level of
 ap> evaluation out of a macro call.  Assume that I have a macro that
 ap> mimics defclass...

One solution here is to use EVAL and backquote.  It's actually not
that ugly.  Here goes:

(pprint
  (macroexpand-1
    (let ((my-class (gensym "CLASS")))
      `(my-defclass ,my-class () (this that other)))))

In your deftest form, you'd stick the eval in:

(deftest test-my-defclass ()
  (let ((class-name (gensym "CLASS")))
    (eval `(my-defclass ,class-name ...))))

I imagine there's a better way, and I'm guessing that you implicitly
wanted to avoid this, and that's why you posted.  But it never hurts
to suggest it.

dave
From: Alain Picard
Subject: Re: How to test a macro?
Date: 
Message-ID: <861yqnnk3x.fsf@gondolin.local.net>
David Bakhash <·····@alum.mit.edu> writes:

> >>>>> "ap" == Alain Picard <·······@optushome.com.au> writes:
> 
>  ap> I am having trouble figuring out how to get an extra level of
>  ap> evaluation out of a macro call.  Assume that I have a macro that
>  ap> mimics defclass...
> 
> One solution here is to use EVAL and backquote.  It's actually not
> that ugly.  Here goes:
> [SNIP]
> 
> I imagine there's a better way, and I'm guessing that you implicitly
> wanted to avoid this, and that's why you posted.  But it never hurts
> to suggest it.

Yes, indeed.  The problem is that EVAL works in the null environment,
and that my example was perhaps oversimplified; but I also need access
to the current lexical environment where the MY-DEFCLASS is being used.

What we really have is something like

(deftest test-my-defclass ()
  (macrolet 
      ((make-an-owner-class (owner-class-name owned-class-name)
			    ;; something which uses my-defclass here...
			    ;; this is what I don't know how to do
			    (my-defclass ,class-name ...)))

      ((make-an-owned-class (owned-class-name)
			  ;; something which uses my-defclass here...
			  ;; this is what I don't know how to do
			  (my-defclass ,class-name ...)))


    (let ((class-name-1 (gensym "OWNER CLASS"))
	  (class-name-2 (gensym "OWNED CLASS")))
      (make-an-owner-class class-name-1 class-name-2)
      (make-an-owned-class class-name-2)
      (assert something))

    ;; Now try the reverse order
    (let ((class-name-1 (gensym "OWNED CLASS"))
	  (class-name-2 (gensym "OWNER CLASS")))
      (make-an-owned-class class-name-1)
      (make-an-owner-class class-name-2 class-name-1)
      (assert something else))))


Sigh.  The danger of oversimplifying.  You, Dave, know
what my-defclass really is, but others on this group don't.  :-)


-- 
It would be difficult to construe        Larry Wall, in  article
this as a feature.			 <·····················@netlabs.com>
From: ····@cadet.homeip.net
Subject: Re: How to test a macro?
Date: 
Message-ID: <m2zoda0xrs.fsf@cadet.homeip.net>
Alain Picard <·······@optushome.com.au> writes:

> David Bakhash <·····@alum.mit.edu> writes:
> 
> > >>>>> "ap" == Alain Picard <·······@optushome.com.au> writes:
> > 
> >  ap> I am having trouble figuring out how to get an extra level of
> >  ap> evaluation out of a macro call.  Assume that I have a macro that
> >  ap> mimics defclass...
> > 
> > One solution here is to use EVAL and backquote.  It's actually not
> > that ugly.  Here goes:
> > [SNIP]

> Yes, indeed.  The problem is that EVAL works in the null environment,
> and that my example was perhaps oversimplified; but I also need access
> to the current lexical environment where the MY-DEFCLASS is being used.

okay.  Then, for the purposes of the DEFTEST, you can bite the bullet
and stick some declarations:

(let ((symbol (gensym "CLASS)))
  (declare (special symbol))
  ...)

I'm pretty sure that that's not what you want, but you can write a
nice little DYNAMIC-LET macro that automatically declares all of its
bindings to be special, and use that instead.

dave
From: David Bakhash
Subject: Re: How to test a macro?
Date: 
Message-ID: <m3bspo6pbl.fsf@alum.mit.edu>
>>>>> "dave" == dave  <····@cadet.homeip.net> writes:

 dave> I'm pretty sure that that's not what you want, but you can
 dave> write a nice little DYNAMIC-LET macro that automatically
 dave> declares all of its bindings to be special, and use that
 dave> instead.

Of course, if you can take Barry's suggestion and pass that
&environment object around properly (something I've never done), then
that seems like the best solution, though I think that putting
macroexpand the code is unpretty.

On this note, why doesn't EVAL take an optional &environment argument?

dave
From: Kent M Pitman
Subject: Re: How to test a macro?
Date: 
Message-ID: <sfwzod763nm.fsf@world.std.com>
David Bakhash <·····@alum.mit.edu> writes:

> 
> >>>>> "dave" == dave  <····@cadet.homeip.net> writes:
> 
>  dave> I'm pretty sure that that's not what you want, but you can
>  dave> write a nice little DYNAMIC-LET macro that automatically
>  dave> declares all of its bindings to be special, and use that
>  dave> instead.
> 
> Of course, if you can take Barry's suggestion and pass that
> &environment object around properly (something I've never done), then
> that seems like the best solution, though I think that putting
> macroexpand the code is unpretty.
> 
> On this note, why doesn't EVAL take an optional &environment argument?

Because there is no place to get an environment from.

The environments macroexpanders get are compile-time environments, not
runtime environments.  Structurally, these are potentially very different
and not at all interchangeable.

The only thing you'd like be usefully able to control in an eval time
environment would be special variables, and PROGV already gives you the
ability to deal with these.
From: ····@cadet.homeip.net
Subject: Re: How to test a macro?
Date: 
Message-ID: <m2elujp1l6.fsf@cadet.homeip.net>
Kent M Pitman <······@world.std.com> writes:

> David Bakhash <·····@alum.mit.edu> writes:
> 
> > 
> > >>>>> "dave" == dave  <····@cadet.homeip.net> writes:
> > 
> >  dave> I'm pretty sure that that's not what you want, but you can
> >  dave> write a nice little DYNAMIC-LET macro that automatically
> >  dave> declares all of its bindings to be special, and use that
> >  dave> instead.
> > 
> > Of course, if you can take Barry's suggestion and pass that
> > &environment object around properly (something I've never done), then
> > that seems like the best solution, though I think that putting
> > macroexpand the code is unpretty.
> > 
> > On this note, why doesn't EVAL take an optional &environment argument?
> 
> Because there is no place to get an environment from.

This is the whole point.  If the compiler were compiling the code,
then I'm guessing that it knows the environment and can represent it
in the FASL file; if it's code being evaluated (i.e. source), then the 
environment is also available.  But again, I'm not sure.  It was just
a guess.

> The only thing you'd like be usefully able to control in an eval
> time environment would be special variables, and PROGV already gives
> you the ability to deal with these.

Yes.  PROGV is that DYNAMIC-LET that I was talking about.  Always
forget about it since I never use it.  In fact, PROGV does more in
that it evaluates the symbol binding list, so it can be used even more 
widely.  But if Alain decides to dynamically bind the variables he
uses in his EVAL form, then he may still choose to write a DYNAMIC-LET 
macro, and just implement it using PROGV vs. using LET + 
(declare (special ...)).

My point was only that using dynamic bindings would save some trouble.

dave
From: Kent M Pitman
Subject: Re: How to test a macro?
Date: 
Message-ID: <sfw3dayyi3e.fsf@world.std.com>
····@cadet.homeip.net writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > David Bakhash <·····@alum.mit.edu> writes:
> > 
> > > On this note, why doesn't EVAL take an optional &environment argument?
> > 
> > Because there is no place to get an environment from.
> 
> This is the whole point.  If the compiler were compiling the code,
> then I'm guessing that it knows the environment and can represent it
> in the FASL file; if it's code being evaluated (i.e. source), then the 
> environment is also available.  But again, I'm not sure.  It was just
> a guess.

Why don't you give me an example of what you presently cannot do that
you'd like to do, using whatever syntax you think you might plausibly
want to do it...?  It's possible (probable even) that I just don't
understand what you're even asking for.

To help you understand why I believe there is not a way to get the 
environment, consider:

  (let ((x 2))
    (values (the-environment)
            (+ x 2)))

and ask yourself, would it be ok for (the-environment) to return 
#<the-empty-environment>?  Because reasonable compilers probably
compile this the same as:

 (values (the-environment) 4)

after constant folding.  Also, consider:

  (let ((x 2))
    #'(lambda (y) 
        (values (the-environment)
                (+ x y))))

This closure would have to cons, even though the compiler can prove
that there are no references to x which assign x, and it should turn
into 

  #'(lambda (y)
      (values (the-environment) (+ 2 y)))

In general, I'd bet the *most* likely use of this would be to debug
why something wasn't working (possibly even a compiler bug) but since
instrumenting this would cause the compiler to take an utterly different
path, people would seriously confuse themselves into proving that some
set of conditions was true when in fact they were not if the instrumentation
were removed.  So it would lead to extra confusion in that regard.

If this is the kind of thing you're asking for, then I can tell you this
is the reason it's not present--because we had lots of discussion about
these issues a long time ago and decided it didn't lead anywhere we wanted
people to go.

I'm pretty sure Scheme used to have a (the-environment) in some of its
incarnations, if not in the revised report itself.  I think MIT Scheme
had or still has it.

T (Yale Scheme) had a sort of half-breed environment thing called a locale
which was a locally global space which was sort of a lexical net that behaved
like it trapped free references but that wasn't "truly global".  It was a
kind of compromise between these two approaches.

Maclisp had a way to pass environments to eval, but I think it was just an
alist of variable bindings, which is unnnecessary since there are other ways
to do this.  It also had a weird thing called *function which allowed you to
get a thing which was a kind of "call by value-return" (poorly simulated 
call-by-reference) version of function with closures over certain specials.
In the end, progv turned out to be better formed.

Lisp Machine Lisp has PROGW, which I personally prefer to PROGV, because it
takes a single alist instead of a list of variables and separate list of
values.  PROGW environments are easier to set up without consing.

I mention all of these to say that it's not that these kinds of things haven't
been tried. They just didn't lead to enlightenment and many of the approaches
led to complications in compilation.  So the world has become simplified.
From: David Bakhash
Subject: Re: How to test a macro?
Date: 
Message-ID: <m33dayr9eb.fsf@alum.mit.edu>
>>>>> "kmp" == Kent M Pitman <Kent> writes:

 kmp> To help you understand why I believe there is not a way to get
 kmp> the environment, consider:

 kmp>   (let ((x 2)) (values (the-environment) (+ x 2)))

 kmp> and ask yourself, would it be ok for (the-environment) to return
 kmp> #<the-empty-environment>?  Because reasonable compilers probably
 kmp> compile this the same as:

 kmp>  (values (the-environment) 4)

 kmp> after constant folding.

this optimization does not describe why what I merely suggested is
impossible.

 kmp> Maclisp had a way to pass environments to eval, but I think it
 kmp> was just an alist of variable bindings, which is unnnecessary
 kmp> since there are other ways to do this.

in fact, I seem to remember that ACL uses an alist as well.  My point
is that if you ask ACL for it, then you can probably get it, if you
know what the non-exported function/variable is.  Lexical closures are 
no more and no less what I was trying to say in my earlier post.  I do 
understand why EVAL works as it does, and I'm not suggesting that
something like:

CL-USER 26 > 
(progv '(x) '(3)
  (defun f (y) (eval '(+ x y))))
F

CL-USER 27 > (f 10)
Error: The variable X is unbound.
  1 (continue) Try evaluating X again.
  2 Return the value of :X instead.
  3 Specify a value to use this time instead of evaluating X.
  4 Specify a value to set X to.
  5 (abort) Return to level 0.
  6 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other options

CL-USER 28 : 1 > 

behave any differently than it does now.  I'm not 100% on this, but I
realize that making what I suggested work for EVAL would be
nightmarish, and of little practical value.

thanks for the historical perspective and insight.

dave
From: Kent M Pitman
Subject: Re: How to test a macro?
Date: 
Message-ID: <sfwd7a2gkz8.fsf@world.std.com>
David Bakhash <·····@alum.mit.edu> writes:

> >>>>> "kmp" == Kent M Pitman <Kent> writes:
> 
>  kmp> To help you understand why I believe there is not a way to get
>  kmp> the environment, consider:
> 
>  kmp>   (let ((x 2)) (values (the-environment) (+ x 2)))
> 
>  kmp> and ask yourself, would it be ok for (the-environment) to return
>  kmp> #<the-empty-environment>?  Because reasonable compilers probably
>  kmp> compile this the same as:
> 
>  kmp>  (values (the-environment) 4)
> 
>  kmp> after constant folding.
> 
> this optimization does not describe why what I merely suggested is
> impossible.

Yes, it does.  You're just not seeing it.  The only purpose of an environment
object is to influence the things to be evaluated in that environment.  The
only way to know how that influence occurs is to look at the code.  If the
code can be reshaped arbitrarily by the compiler so that the code you see is
not the code to be affected, then you will not know (a) when things you
put in the environment will have no effect, (b) when things you put in the
environment will have unexpected effects, and (c) when there are things you
want to affect but don't know you could be affecting. At that point, what
can it mean?

> Lexical closures are 
> no more and no less what I was trying to say in my earlier post.

I don't understand.  So are you saying you want to pick apart closures
or that having closures, you don't need environments?

> I'm not 100% on this, but I
> realize that making what I suggested work for EVAL would be
> nightmarish, and of little practical value.

That's good, at least.

> thanks for the historical perspective and insight.

Not a problem.
From: David Bakhash
Subject: Re: How to test a macro?
Date: 
Message-ID: <m3vgnuo3i9.fsf@alum.mit.edu>
>>>>> "kmp" == Kent M Pitman <Kent> writes:

 >> Lexical closures are no more and no less what I was trying to say
 >> in my earlier post.

 kmp> I don't understand.  So are you saying you want to pick apart
 kmp> closures or that having closures, you don't need environments?

I have to re-think this.  I'm uncertain.  I was thinking that whatever
lexical closures use to capture the lexical environment could also be
used here.

dave
From: Raymond Wiker
Subject: Re: How to test a macro?
Date: 
Message-ID: <86n19bacio.fsf@raw.grenland.fast.no>
Alain Picard <·······@optushome.com.au> writes:

> Dear lispers,
> 
> I am having trouble figuring out how to get an extra
> level of evaluation out of a macro call.  Assume that
> I have a macro that mimics defclass:
> 
> (defmacro my-defclass (class-name supers slots . options)
>   `(defclass ,class-name ,supers ,slots ,@options))
> 
> (My macro definition does much more than this; however,
> this simple definition will illustrate my problem.)
> 
> I want to write a test for my-defclass where the name
> of the class is a gensym'd symbol.  E.g.,
> 
> (deftest test-my-defclass ()
>   (let ((class-name (gensym "CLASS")))
>     (my-defclass class-name ...)))
> 
> The problem should be obvious: the first arg (all args,
> actually) to my-defclass is not evaluated and I can't
> get to the gensym'd class name that I want to use.  I've
> explored using macrolet and symbol-macrolet; but, so far,
> I have been unsuccessful in accomplishing what I'm trying
> to do.  There has to be some way of doing this ... we are
> talking about lisp here, afterall :-).  Can anyone help?

        define-compiler-macro *may* be a solution:

(define-compiler-macro deftest (&whole form &rest args)
    (if (and args  
	     (eq (car args) 'test-my-defclass))
	(let ((class-name (gensym "CLASS")))
	  `(my-defclass ,class-name))
	form)))

        The obvious disadvantage of this is that it conflicts with
other (possible) uses of define-compiler-macro for deftest. This could
be overcome by making the compiler macro for deftest use an
(extendable) transformation table.

        It's also possible that this use of define-compiler-macro is a
*really bad idea* (TM) - if so, I suspect that I'll hear about it :-)

        //Raymond.

-- 
Raymond Wiker
·············@fast.no
From: Raymond Wiker
Subject: Re: How to test a macro?
Date: 
Message-ID: <86itjza7j0.fsf@raw.grenland.fast.no>
Raymond Wiker <·············@fast.no> writes:

> 
>         define-compiler-macro *may* be a solution:
> 
> (define-compiler-macro deftest (&whole form &rest args)
>     (if (and args  
> 	     (eq (car args) 'test-my-defclass))
> 	(let ((class-name (gensym "CLASS")))
> 	  `(my-defclass ,class-name))
> 	form)))

        Ugh. This is obviously wrong. Sorry.

-- 
Raymond Wiker
·············@fast.no
From: Lieven Marchand
Subject: Re: How to test a macro?
Date: 
Message-ID: <m3lmovwnkv.fsf@localhost.localdomain>
Alain Picard <·······@optushome.com.au> writes:

> I am having trouble figuring out how to get an extra
> level of evaluation out of a macro call.  Assume that
> I have a macro that mimics defclass:
> 
> (defmacro my-defclass (class-name supers slots . options)
>   `(defclass ,class-name ,supers ,slots ,@options))
> 
> (My macro definition does much more than this; however,
> this simple definition will illustrate my problem.)
> 
> I want to write a test for my-defclass where the name
> of the class is a gensym'd symbol.  E.g.,
> 
> (deftest test-my-defclass ()
>   (let ((class-name (gensym "CLASS")))
>     (my-defclass class-name ...)))
> 
> The problem should be obvious: the first arg (all args,
> actually) to my-defclass is not evaluated and I can't
> get to the gensym'd class name that I want to use.  I've
> explored using macrolet and symbol-macrolet; but, so far,
> I have been unsuccessful in accomplishing what I'm trying
> to do.  There has to be some way of doing this ... we are
> talking about lisp here, afterall :-).  Can anyone help?

At first glance, I see two ways to do this. Probably the easiest is to
use the functional layer of CLOS. In stead of DEFCLASS, use
ENSURE-CLASS. In most implementations, you'll find this in a package
MOP or CLOS. Unfortunately, when CLOS was imported in ANSI rather
raggedly, ENSURE-CLASS didn't make it, so this is not completely
standard.

An other way to do this is calling MACROEXPAND on `(my-defclass
,class-name ...) in deftest.

-- 
Lieven Marchand <···@wyrd.be>
Gla�r ok reifr skyli gumna hverr, unz sinn b��r bana.