From: grackle
Subject: Forms vs. values in macros
Date: 
Message-ID: <1162567994.402757.228390@b28g2000cwb.googlegroups.com>
I'm trying to write a macro that creates rt tests for me.  Let's
pretend I'm testing the accessors of a structure "point" with members x
and y.  I start out like this:

(defstruct point x y)
(require 'rt)
(rt:rem-all-tests)

Here's the long way to write a test:

(rt:deftest zero-point-x (point-x (make-point :x 0 :y 0)) 0)
(rt:deftest zero-point-y (point-y (make-point :x 0 :y 0)) 0)

I'd rather write:

(make-point-test zero 0 0)

Here's a macro that accomplishes that:

(defmacro make-point-test (name x y)
  `(rt:deftest
    ,(concatenate 'string (symbol-name name) "-point-x")
    (point-x (make-point :x ,x :y ,y))
    ,x)
  `(rt:deftest
    ,(concatenate 'string (symbol-name name) "-point-y")
    (point-y (make-point :x ,x :y ,y))
    ,y))

Now the hard part.  Sometimes I want to use a complex form to specify
the value for a test.  For example, suppose I know that 13 * 75 is a
tricky value to store in a point structure.  I try this:

(make-point-test tricky 0 (* 13 75))

Oops.  This expands to

(rt:deftest tricky-point-x (point-x (make-point :x 0 :y (* 13 75))) 0)
(rt:deftest tricky-point-y
    (point-y (make-point :x 0 :y (* 13 75))) (* 13 75))

Running tricky-point-y results in:

Test "TRICKY-POINT-Y" failed.
Form:  (POINT-Y (MAKE-POINT :X 0 :Y (* 13 75)))
Expected value:  (* 13 75)
Actual value:  975

I can write my test as (make-point-test tricky 0 975), but then it
isn't clear where 975 came from.  Adding a comment creates redundancy:
(make-point-test tricky 0 975) ; 975 is 13 * 75

Two questions:

1.  How can I write make-point-test so it does what I want?
2.  point-y returns a value, not a form.  How does rt:do-test compare
the value returned by point-y to the form specified in rt:deftest?  (I
assume it doesn't eval the form to get a value, because that would
result in my test succeeding.)

Thanks,
David

From: grackle
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <1162572963.777702.325650@k70g2000cwa.googlegroups.com>
After all that, I answered my own question.  I realized I was getting
hung up on the difficulty of translating between forms (code) and
values, trying to bridge a gap whose nonexistence is my reason for
learning Lisp in the first place.  However, my solution uses eval,
which makes it automatically suspect:

(defmacro make-point-test (name x y)
  `(rt:deftest
    ,(concatenate 'string (symbol-name name) "-point-x")
    (point-x (make-point :x ,x :y ,y))
    ,(eval x))
  `(rt:deftest
    ,(concatenate 'string (symbol-name name) "-point-y")
    (point-y (make-point :x ,x :y ,y))
    ,(eval y)))

Is this an appropriate use of eval?

Thanks,
David
From: Thomas A. Russ
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <ymiejsga50p.fsf@sevak.isi.edu>
"grackle" <···········@gmail.com> writes:

> After all that, I answered my own question.  I realized I was getting
> hung up on the difficulty of translating between forms (code) and
> values, trying to bridge a gap whose nonexistence is my reason for
> learning Lisp in the first place.  However, my solution uses eval,
> which makes it automatically suspect:
> 
> (defmacro make-point-test (name x y)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-x")
>     (point-x (make-point :x ,x :y ,y))
>     ,(eval x))
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-y")
>     (point-y (make-point :x ,x :y ,y))
>     ,(eval y)))
> 
> Is this an appropriate use of eval?

Well, there are a couple of problems with this.  One of them is that you
are doing the EVAL at macro-expansion time.  This is fine as long as the
expression is effectively a constant.  But if that is true, you might as
well save yourself the trouble and just use the #. reader evaluation
form I described in an earlier post.

You can, and should, find a way to avoid having to do this.  We don't
know what RT:DEFTEST expands into, so it is difficult to say exactly
what should be changed, but it is clear that the fundamental problem is
that the value of X or Y, respectively IS evaluated in the MAKE-POINT
form, but not in whatever you use for a comparison form.  Are you
explicitly adding a QUOTE in RT:DEFTEST?  If so, you may need a separate
version that doesn't do that, which you can invoke from the
MAKE-POINT-TEST macro.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Ken Tilton
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <MAO2h.461$AQ6.360@newsfe11.lga>
grackle wrote:
> I'm trying to write a macro that creates rt tests for me.  Let's
> pretend I'm testing the accessors of a structure "point" with members x
> and y.  I start out like this:
> 
> (defstruct point x y)
> (require 'rt)
> (rt:rem-all-tests)
> 
> Here's the long way to write a test:
> 
> (rt:deftest zero-point-x (point-x (make-point :x 0 :y 0)) 0)
> (rt:deftest zero-point-y (point-y (make-point :x 0 :y 0)) 0)
> 
> I'd rather write:
> 
> (make-point-test zero 0 0)
> 
> Here's a macro that accomplishes that:
> 
> (defmacro make-point-test (name x y)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-x")
>     (point-x (make-point :x ,x :y ,y))
>     ,x)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-y")
>     (point-y (make-point :x ,x :y ,y))
>     ,y))
> 
> Now the hard part.  Sometimes I want to use a complex form to specify
> the value for a test.  For example, suppose I know that 13 * 75 is a
> tricky value to store in a point structure.  I try this:
> 
> (make-point-test tricky 0 (* 13 75))
> 
> Oops.  This expands to
> 
> (rt:deftest tricky-point-x (point-x (make-point :x 0 :y (* 13 75))) 0)
> (rt:deftest tricky-point-y
>     (point-y (make-point :x 0 :y (* 13 75))) (* 13 75))
> 
> Running tricky-point-y results in:
> 
> Test "TRICKY-POINT-Y" failed.
> Form:  (POINT-Y (MAKE-POINT :X 0 :Y (* 13 75)))
> Expected value:  (* 13 75)
> Actual value:  975
> 
> I can write my test as (make-point-test tricky 0 975), but then it
> isn't clear where 975 came from.  Adding a comment creates redundancy:
> (make-point-test tricky 0 975) ; 975 is 13 * 75
> 
> Two questions:
> 
> 1.  How can I write make-point-test so it does what I want?
> 2.  point-y returns a value, not a form.  How does rt:do-test compare
> the value returned by point-y to the form specified in rt:deftest? 

I suggest you macroexpand the troublesome deftest form and you will find 
out. (I do not use deftest so i do not know, but as you guessed it seems 
to quote the expected value argument.) And I offer this suggestion not 
to be a PITA, but to help you along the learning curve.

If deftest cannot do any better than that (is there some other API 
entrypoint that evaluates the expected result?) you may have to write 
your own or hack it a little. You can do what I did with Cells, 
something roughly like:

(defmacro test-compare (v1 v2)
    (with-gensms (v1v v2v)
      `(let ((,v1v ,v1)(,v2v ,v2))
          `(unless (eql ,v1v ,v2v)
              (break "~a (from ~a) is not at all like ~a (from ~a)"
                     ,v1v ',v1 ,v2v ',v2)))))

Just typed in, so hardly tested.

hth, kt

-- 
Cells: http://common-lisp.net/project/cells/

"I'll say I'm losing my grip, and it feels terrific."
    -- Smiling husband to scowling wife, New Yorker cartoon
From: Pascal Bourguignon
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <87r6wko0a9.fsf@thalassa.informatimago.com>
"grackle" <···········@gmail.com> writes:

> I'm trying to write a macro that creates rt tests for me.  Let's
> pretend I'm testing the accessors of a structure "point" with members x
> and y.  I start out like this:
>
> (defstruct point x y)
> (require 'rt)
> (rt:rem-all-tests)
>
> Here's the long way to write a test:
>
> (rt:deftest zero-point-x (point-x (make-point :x 0 :y 0)) 0)
> (rt:deftest zero-point-y (point-y (make-point :x 0 :y 0)) 0)
>
> I'd rather write:
>
> (make-point-test zero 0 0)
>
> Here's a macro that accomplishes that:
>
> (defmacro make-point-test (name x y)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-x")
>     (point-x (make-point :x ,x :y ,y))
>     ,x)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-y")
>     (point-y (make-point :x ,x :y ,y))
>     ,y))

What if you wrote a function like:

(defun silly ()
  1
  2
  3
  4)

What would it do?

> Now the hard part.  Sometimes I want to use a complex form to specify
> the value for a test.  For example, suppose I know that 13 * 75 is a
> tricky value to store in a point structure.  I try this:
>
> (make-point-test tricky 0 (* 13 75))
>
> Oops.  This expands to
>
> (rt:deftest tricky-point-x (point-x (make-point :x 0 :y (* 13 75))) 0)
> (rt:deftest tricky-point-y
>     (point-y (make-point :x 0 :y (* 13 75))) (* 13 75))

No, not really.


> Running tricky-point-y results in:
>
> Test "TRICKY-POINT-Y" failed.
> Form:  (POINT-Y (MAKE-POINT :X 0 :Y (* 13 75)))
> Expected value:  (* 13 75)
> Actual value:  975
>
> I can write my test as (make-point-test tricky 0 975), but then it
> isn't clear where 975 came from.  Adding a comment creates redundancy:
> (make-point-test tricky 0 975) ; 975 is 13 * 75
>
> Two questions:
>
> 1.  How can I write make-point-test so it does what I want?

What does rt:deftest return?  
Can you test whether the test was successful or not?

In my opinion, the error is to use rt:deftest in the first place.
You should write your own testbed...

If you really insist on using rt:deftest, I guess you could write it as:

(defmacro do-test (name expression expected)
   (let ((vexpval (gensym)))
    `(let ((,vexpval ,expected))
       (print (list 'testing ',name ',expression ',expected = ',vexpval))
       (funcall (compile nil 
              (list 'lambda '() (list 'rt:deftest ',name ',expression ,vexpval)))))))

(macroexpand '(do-test aaa (+ 1 1 1) (+ 1 2)))
-->
(LET ((#:G6857 (+ 1 2)))
 (PRINT (LIST 'TESTING 'AAA '(+ 1 1 1) '(+ 1 2) = '#:G6857))
 (FUNCALL
  (COMPILE NIL (LIST 'LAMBDA 'NIL (LIST 'RT:DEFTEST 'AAA '(+ 1 1 1) #:G6857))))) ;
T

(defmacro make-point-test (name x y)
   `(progn
       (do-test  ,(concatenate 'string (symbol-name name) "-point-x")
                  (point-x (make-point :x ,x :y ,y))
                 ,x)
       (do-test  ,(concatenate 'string (symbol-name name) "-point-y")
                  (point-y (make-point :x ,x :y ,y))
                 ,y)))


> 2.  point-y returns a value, not a form.  How does rt:do-test compare
> the value returned by point-y to the form specified in rt:deftest?  (I
> assume it doesn't eval the form to get a value, because that would
> result in my test succeeding.)

Probably with EQL.  Why don't you read the doc or the source of rt:deftest?


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: grackle
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <1162586673.519667.245750@h54g2000cwb.googlegroups.com>
Pascal Bourguignon wrote:
> In my opinion, the error is to use rt:deftest in the first place.
> You should write your own testbed...

For the experience or for another reason?

> Probably with EQL.  Why don't you read the doc or the source of rt:deftest?

I didn't understand it.  I couldn't figure out how things went from
code to data and back again, which is a silly thing to be concerned
about in Lisp *slaps head*  The right question is what gets evaled
when, and how many times that happens between point A and point B, and
I'm starting to get the hang of that now.

Sidney Markowitz wrote:
>  (make-point-test tricky 0 #.(* 13 75))

That's perfect for I'm doing right now.

> I'm assuming that there will be times that you will not know the value
> at compile time so you do not want the macro to always evaluate it when
> it macroexpands.

Pascal's version seems to work for this (called do-test in his post,
pascal-test here):

(setf *tricky-y* 975)
(pascal-test tricky-1 0 *tricky-y*) ; adds a test with y = 975
(setf *tricky-y* 40)
(pascal-test tricky-2 0 *tricky-y*) ; adds a test with y = 40

I didn't understand Pascal's version at first, but it prompted me to
try this:

* (setf foo 'bar)
BAR
* `',foo
'BAR

I think I have an Aha! pending... at least I hope so :-)

Thanks for the help, guys.

-David
From: Pascal Bourguignon
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <878xisqf90.fsf@thalassa.informatimago.com>
"grackle" <···········@gmail.com> writes:

> Pascal Bourguignon wrote:
>> In my opinion, the error is to use rt:deftest in the first place.
>> You should write your own testbed...
>
> For the experience or for another reason?

Obviously, rt:deftest doesn't do what you want. 
It doesn't displays what you want.


>> Probably with EQL.  Why don't you read the doc or the source of rt:deftest?
>
> I didn't understand it.  I couldn't figure out how things went from
> code to data and back again, which is a silly thing to be concerned
> about in Lisp *slaps head*  The right question is what gets evaled
> when, and how many times that happens between point A and point B, and
> I'm starting to get the hang of that now.
>
> Sidney Markowitz wrote:
>>  (make-point-test tricky 0 #.(* 13 75))
>
> That's perfect for I'm doing right now.

If that's perfect, why were you complaining:

>>> Now the hard part.  Sometimes I want to use a complex form to specify
>>> the value for a test.  For example, suppose I know that 13 * 75 is a
>>> tricky value to store in a point structure.  I try this:
>>> 
>>> (make-point-test tricky 0 (* 13 75))
>>> 
>>> Oops.  This expands to
>>> 
>>> (rt:deftest tricky-point-x (point-x (make-point :x 0 :y (* 13 75))) 0)
>>> (rt:deftest tricky-point-y
>>>     (point-y (make-point :x 0 :y (* 13 75))) (* 13 75))
>>> 
>>> Running tricky-point-y results in:
>>> 
>>> Test "TRICKY-POINT-Y" failed.
>>> Form:  (POINT-Y (MAKE-POINT :X 0 :Y (* 13 75)))
>>> Expected value:  (* 13 75)
>>> Actual value:  975
>>> 
>>> I can write my test as (make-point-test tricky 0 975), but then it
>>> isn't clear where 975 came from.  Adding a comment creates redundancy:
>>> (make-point-test tricky 0 975) ; 975 is 13 * 75

#.(* 13 75) will still appear as 975 and you still won't know where it
comes from.



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: grackle
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <1162599460.491112.22420@h54g2000cwb.googlegroups.com>
Pascal Bourguignon wrote:
> #.(* 13 75) will still appear as 975 and you still won't know where it
> comes from.

It will appear as #.(* 13 75) in my code, which is good enough.  I
expect to write lots of tests, but I don't expect them to fail very
often :-)

-David
From: Sidney Markowitz
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <454b8583$0$88645$742ec2ed@news.sonic.net>
grackle wrote, On 4/11/06 4:33 AM:
> Now the hard part.  Sometimes I want to use a complex form to specify
> the value for a test.  For example, suppose I know that 13 * 75 is a
> tricky value to store in a point structure.  I try this:
> 
> (make-point-test tricky 0 (* 13 75))

This is a case in which you know the value at compile time and you want
to express it in a way that documents where it came from.

That is something you might want to do independent of whether this is a
macro or a function definition. The standard way to do that is to use a
reader macro

 (make-point-test tricky 0 #.(* 13 75))

I'm assuming that there will be times that you will not know the value
at compile time so you do not want the macro to always evaluate it when
it macroexpands.

-- 
    Sidney Markowitz
    http://www.sidney.com
From: Thomas A. Russ
Subject: Re: Forms vs. values in macros
Date: 
Message-ID: <ymiirhsa598.fsf@sevak.isi.edu>
"grackle" <···········@gmail.com> writes:

> I'm trying to write a macro that creates rt tests for me.  Let's
> pretend I'm testing the accessors of a structure "point" with members x
> and y.  I start out like this:
> 
> (defstruct point x y)
> (require 'rt)
> (rt:rem-all-tests)
> 
> Here's the long way to write a test:
> 
> (rt:deftest zero-point-x (point-x (make-point :x 0 :y 0)) 0)
> (rt:deftest zero-point-y (point-y (make-point :x 0 :y 0)) 0)
> 
> I'd rather write:
> 
> (make-point-test zero 0 0)
> 
> Here's a macro that accomplishes that:

No it doesn't.  

> (defmacro make-point-test (name x y)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-x")
>     (point-x (make-point :x ,x :y ,y))
>     ,x)
>   `(rt:deftest
>     ,(concatenate 'string (symbol-name name) "-point-y")
>     (point-y (make-point :x ,x :y ,y))
>     ,y))

Macros only return a single form.  What this macro does is create the
first form (the "-point-x" one) and then effectively throw it away.
Only the second form is returned.  If you need to return multiple pieces
of code from a macro, you have to wrap it in a PROGN form.

> Now the hard part.  Sometimes I want to use a complex form to specify
> the value for a test.  For example, suppose I know that 13 * 75 is a
> tricky value to store in a point structure.  I try this:
> 
> (make-point-test tricky 0 (* 13 75))

I would think that the simplest way to do this is to just use the
#. reader macro and write

  (make-point-test tricky 0 #.(* 13 75))

That will cause read-time evaluation of the expression and you can use
it.

-- 
Thomas A. Russ,  USC/Information Sciences Institute