From: Peter Seibel
Subject: Critique my code?
Date: 
Message-ID: <m2vgaj55vx.fsf@javamonkey.com>
Hello. After doing some reading (Paul Graham's books and part of
PAIP), I've been trying my hand at some actual Lisp programming. Below
is a little test framework I wrote to use in writing unit tests for my
other code. Since it's small and self-contained I was hoping that some
of the folks here might be willing to critique it. I'm really open to
criticism at any level: from style and formatting nits to pointing out
deep lossage in the design; I want to develop an idiomatic style and
that can only be achieved by interacting with "native speakers".

Particular questions I have about this code are:

 - Is my use of special variables justified?

 - Is this an appropriate use of macros?

 - Is there some simpler way to do what I'm doing in the line in
 'check' that starts: '(let ((,bindings (list ...'?

The main code is in two macros, run-tests and check. I hope the
docstrings are sufficient explanation of what I'm trying to do. Below
the two macros is a trivial example of how I'd use them.

Thanks for any and all comments on this code. And apologies for any
stomach-turning abuses of the langauge.

-Peter

(defpackage "TESTING"
  (:use "COMMON-LISP")
  (:export "RUN-TESTS" "CHECK"))

(in-package "TESTING")

(defmacro run-tests (&body body)
  "Prepare to run a set of tests. Calls to the check macro must be
wrapped, at some level, in a call to this macro. Prints the number of
passes and failures and returns true if there were no failures."
  `(let ((passes 0) (fails 0))
     (declare (special passes fails))
     ,@body
     (format t "~D passes; ~D failures~%" passes fails)
     (= fails 0)))
  
(defmacro check (&body forms)
  "Evaluate a set of forms and report pass or fail depending on
whether they evaluate to true (non nil) or false. Additionally you can
include the forms (:show ...) and (:show-with ...) to control how the
forms are displayed in the results. The items after :show should be
symbols that you want to see the values of in the report. After
:show-with you can list dotted-pairs consisting of symbols and the
form that should be used to get the representation for that symbol."
  (flet ((control-p (form) (keywordp (first form)))
	 (extract-bound-forms (control-items)
	   (loop for c in control-items
	     when (eq (first c) :show) append (mapcar (lambda (c) (cons c c)) (rest c))
	     when (eq (first c) :show-with) append (rest c))))
    (let ((bindings (gensym))
	  (tests (remove-if #'control-p forms))
	  (bound-forms (extract-bound-forms (remove-if-not #'control-p forms))))
      `(prog ()
	 (declare (special passes fails))
         (let ((,bindings (list ,@(mapcar (lambda (symbol) `(cons ',symbol ,(cdr (assoc symbol bound-forms)))) (mapcar #'car bound-forms)))))
	   (flet ((pass-fail (result form)
		    (let ((label (if result "pass" "fail")))
                      (format t "~A ... ~A~%" label form)
		      (if result (incf passes) (incf fails)))))
	     ,@(mapcar (lambda (test) `(pass-fail ,test (sublis ,bindings ',test))) tests)))))))



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Example of use

(defun some-function (x) (* 2 x))

(defun check-some-function (arg expected)
  (testing:check
   (:show arg expected)
   (eq (some-function arg) expected)))

(defun check-some-function-2 (arg expected)
  (testing:check
   (:show arg)
   (:show-with (expected . (format nil "expected: ~X" expected)))
   (eq (some-function arg) expected)))


[60]> 
(testing:run-tests
 (testing:check (eq (+ 1 2) 3))
 (check-some-function 2 4)
 (check-some-function 10 20)
 (check-some-function 5 11) ; to show failure.
 (check-some-function-2 30 60))
pass ... (EQ (+ 1 2) 3)
pass ... (EQ (SOME-FUNCTION 2) 4)
pass ... (EQ (SOME-FUNCTION 10) 20)
fail ... (EQ (SOME-FUNCTION 5) 11)
pass ... (EQ (SOME-FUNCTION 30) expected: 3C)
4 passes; 1 failures
NIL
[61]> 


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

From: Wade Humeniuk
Subject: Re: Critique my code?
Date: 
Message-ID: <aa464r$bqm$1@news3.cadvision.com>
"Peter Seibel" <·····@javamonkey.com> wrote in message
···················@javamonkey.com...
>
> (defmacro run-tests (&body body)
>   "Prepare to run a set of tests. Calls to the check macro must be
> wrapped, at some level, in a call to this macro. Prints the number of
> passes and failures and returns true if there were no failures."
>   `(let ((passes 0) (fails 0))
>      (declare (special passes fails))
>      ,@body
>      (format t "~D passes; ~D failures~%" passes fails)
>      (= fails 0)))
>

My main comment is about the approach with the macro run-tests.
Personally I would use a handler-case to encapsulate each check.  Then each check can be
run and can be recovered no matter what happens in each test, whether a test or fail, or
whether some exception occurs.  I am assumming that you are going to use the code for some
automated testing procedures.

I also rewrote the code in how I would write it.  I did away with show and show-with and
replaced them with on-pass and on-fail keyword args to the check macro.  On pass of
failure, forms are conditionally called to do any specific check processing.  Things look
a little differently than what you did.

Wade

(defpackage "TESTING"
  (:use "COMMON-LISP")
  (:export "RUN-TESTS" "CHECK"))

(in-package "TESTING")

(defvar *test-output-stream* t)
(defvar *pass-fail-handler* 'identity)

(defmacro check (form &key on-fail
                      on-pass
                      (stream '*test-output-stream*))
  `(let* ((print-form ',(identity form))
          (pass (handler-case ,(identity form)
                  (error (condition)
                         (format ,stream "**ERROR** ~S" condition)
                         nil))))
     (funcall *pass-fail-handler* pass)
     (format ,stream "Check: Test Form ~S ~A~%"
             print-form
             (if pass "Passes" "Fails"))
      (if pass ,(identity on-pass) ,(identity on-fail))
      pass))

(defmacro run-tests (&body body)
  (let* ((passes (gensym))
         (fails (gensym)))
    `(let* ((,passes 0) (,fails 0)
            (*pass-fail-handler*
             (lambda (result)
               (if result (incf ,passes) (incf ,fails))
               result)))
       ,@body
       (format *test-output-stream* "~D passes; ~D failures~%" ,passes ,fails)
       (= ,fails 0))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Example of use

(defun some-function (x) (* 2 x))

(defun check-some-function (arg expected)
  (eq (some-function arg) expected))

(run-tests
 (check (eq (+ 1 2) 3))
 (check (check-some-function 2 4))
 (check (string-equal "Test" 10))
 (check (check-some-function 10 20))
 (check (check-some-function 5 11) :on-fail (format t "UNEQ for 5 11~%")))

#| Results

TESTING 48 > (run-tests
 (check (eq (+ 1 2) 3))
 (check (check-some-function 2 4))
 (check (string-equal "Test" 10))
 (check (check-some-function 10 20))
 (check (check-some-function 5 11) :on-fail (format t "UNEQ for 5 11~%")))

Check: Test Form (EQ (+ 1 2) 3) Passes
Check: Test Form (CHECK-SOME-FUNCTION 2 4) Passes
**ERROR** #<CONDITIONS:TYPE-COERCION-ERROR 2140A844>Check: Test Form (STRING-EQUAL "Test"
10) Fails
Check: Test Form (CHECK-SOME-FUNCTION 10 20) Passes
Check: Test Form (CHECK-SOME-FUNCTION 5 11) Fails
UNEQ for 5 11
3 passes; 2 failures
NIL

TESTING 49 >
|#
From: Bulent Murtezaoglu
Subject: Re: Critique my code?
Date: 
Message-ID: <87y9feqowz.fsf@nkapi.internal>
>>>>> "WH" == Wade Humeniuk <········@cadvision.com> writes:
[...]
    WH> Check: Test Form (EQ (+ 1 2) 3) Passes [...]

Minor point but worth making since this is a "learning" thread: eq is not
guaranteed to work on numbers, works for most implementations, but
probably eql or = is more appropriate here.  (eql and = do not behave the
same way for numbers, but at least their behaviour for numbers is
specified by the spec.  See the Hyperspec for details).

cheers,

BM
From: Wade Humeniuk
Subject: Re: Critique my code?
Date: 
Message-ID: <aa4c0e$dp0$1@news3.cadvision.com>
"Bulent Murtezaoglu" <··@acm.org> wrote in message ···················@nkapi.internal...
> >>>>> "WH" == Wade Humeniuk <········@cadvision.com> writes:
> [...]
>     WH> Check: Test Form (EQ (+ 1 2) 3) Passes [...]
>
> Minor point but worth making since this is a "learning" thread: eq is not
> guaranteed to work on numbers, works for most implementations, but
> probably eql or = is more appropriate here.  (eql and = do not behave the
> same way for numbers, but at least their behaviour for numbers is
> specified by the spec.  See the Hyperspec for details).

Yes indeed,

Corrected version below.

BTW in this code check is independent of run-tests.

Wade

TESTING 7 > (check (= 10 20)) ;check  is independent of run-tests
Check: Test Form (= 10 20) Fails
NIL

TESTING 8 > (run-tests (check (= 10 20)))
Check: Test Form (= 10 20) Fails
0 passes; 1 failures
NIL

TESTING 9 >

;; Code

(defpackage "TESTING"
  (:use "COMMON-LISP")
  (:export "RUN-TESTS" "CHECK"))

(in-package "TESTING")

(defvar *test-output-stream* t)
(defvar *pass-fail-handler* 'identity)

(defmacro check (form &key on-fail
                      on-pass
                      (stream '*test-output-stream*))
  `(let* ((print-form ',(identity form))
          (pass (handler-case ,(identity form)
                  (error (condition)
                         (format ,stream "**ERROR** ~S" condition)
                         nil))))
     (funcall *pass-fail-handler* pass)
     (format ,stream "Check: Test Form ~S ~A~%"
             print-form
             (if pass "Passes" "Fails"))
      (if pass ,(identity on-pass) ,(identity on-fail))
      pass))

(defmacro run-tests (&body body)
  (let* ((passes (gensym))
         (fails (gensym)))
    `(let* ((,passes 0) (,fails 0)
            (*pass-fail-handler*
             (lambda (result)
               (if result (incf ,passes) (incf ,fails))
               result)))
       ,@body
       (format *test-output-stream* "~D passes; ~D failures~%" ,passes ,fails)
       (= ,fails 0))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Example of use

(defun some-function (x) (* 2 x))

(defun check-some-function (arg expected)
  (eql (some-function arg) expected))

(run-tests
 (check (= (+ 1 2) 3))
 (check (check-some-function 2 4))
 (check (string-equal "Test" 10))
 (+ 10 (check 10))
 (check (check-some-function 10 20))
 (check (check-some-function 5 11) :on-fail (format t "Second Arg should be 2X first
arg~%")))

#| Results
TESTING 6 > (run-tests
 (check (= (+ 1 2) 3))
 (check (check-some-function 2 4))
 (check (string-equal "Test" 10))
 (+ 10 (check 10))
 (check (check-some-function 10 20))
 (check (check-some-function 5 11) :on-fail (format t "Second Arg should be 2X first
arg~%")))

Check: Test Form (= (+ 1 2) 3) Passes
Check: Test Form (CHECK-SOME-FUNCTION 2 4) Passes
**ERROR** #<CONDITIONS:TYPE-COERCION-ERROR 2042A24C>Check: Test Form (STRING-EQUAL "Test"
10) Fails
Check: Test Form 10 Passes
Check: Test Form (CHECK-SOME-FUNCTION 10 20) Passes
Check: Test Form (CHECK-SOME-FUNCTION 5 11) Fails
Second Arg should be 2X first arg
4 passes; 2 failures
NIL

TESTING 7 > (check (= 10 20)) ;check is independent of run-tests
Check: Test Form (= 10 20) Fails
NIL

TESTING 8 >

|#
From: Peter Seibel
Subject: Re: Critique my code?
Date: 
Message-ID: <m2pu0p68hi.fsf@javamonkey.com>
"Wade Humeniuk" <········@cadvision.com> writes:

> "Peter Seibel" <·····@javamonkey.com> wrote in message
> ···················@javamonkey.com...
> >
> > (defmacro run-tests (&body body)
> >   "Prepare to run a set of tests. Calls to the check macro must be
> > wrapped, at some level, in a call to this macro. Prints the number of
> > passes and failures and returns true if there were no failures."
> >   `(let ((passes 0) (fails 0))
> >      (declare (special passes fails))
> >      ,@body
> >      (format t "~D passes; ~D failures~%" passes fails)
> >      (= fails 0)))
> >
> 
> My main comment is about the approach with the macro run-tests.
> Personally I would use a handler-case to encapsulate each check.
> Then each check can be run and can be recovered no matter what
> happens in each test, whether a test or fail, or whether some
> exception occurs. I am assumming that you are going to use the code
> for some automated testing procedures.
> 
> I also rewrote the code in how I would write it. I did away with
> show and show-with and replaced them with on-pass and on-fail
> keyword args to the check macro. On pass of failure, forms are
> conditionally called to do any specific check processing. Things
> look a little differently than what you did.

Thanks. Wrapping things in handler-case is definitely a good idea and
there are several other ideas there that I think I get the point of.
I'm not sure I understand your use of the 'identity' function. How is
,(identity foo) different from just plain ,foo?

-Peter


> 
> Wade
> 
> (defpackage "TESTING"
>   (:use "COMMON-LISP")
>   (:export "RUN-TESTS" "CHECK"))
> 
> (in-package "TESTING")
> 
> (defvar *test-output-stream* t)
> (defvar *pass-fail-handler* 'identity)
> 
> (defmacro check (form &key on-fail
>                       on-pass
>                       (stream '*test-output-stream*))
>   `(let* ((print-form ',(identity form))
>           (pass (handler-case ,(identity form)
>                   (error (condition)
>                          (format ,stream "**ERROR** ~S" condition)
>                          nil))))
>      (funcall *pass-fail-handler* pass)
>      (format ,stream "Check: Test Form ~S ~A~%"
>              print-form
>              (if pass "Passes" "Fails"))
>       (if pass ,(identity on-pass) ,(identity on-fail))
>       pass))
> 
> (defmacro run-tests (&body body)
>   (let* ((passes (gensym))
>          (fails (gensym)))
>     `(let* ((,passes 0) (,fails 0)
>             (*pass-fail-handler*
>              (lambda (result)
>                (if result (incf ,passes) (incf ,fails))
>                result)))
>        ,@body
>        (format *test-output-stream* "~D passes; ~D failures~%" ,passes ,fails)
>        (= ,fails 0))))
> 
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> ;; Example of use
> 
> (defun some-function (x) (* 2 x))
> 
> (defun check-some-function (arg expected)
>   (eq (some-function arg) expected))
> 
> (run-tests
>  (check (eq (+ 1 2) 3))
>  (check (check-some-function 2 4))
>  (check (string-equal "Test" 10))
>  (check (check-some-function 10 20))
>  (check (check-some-function 5 11) :on-fail (format t "UNEQ for 5 11~%")))
> 
> #| Results
> 
> TESTING 48 > (run-tests
>  (check (eq (+ 1 2) 3))
>  (check (check-some-function 2 4))
>  (check (string-equal "Test" 10))
>  (check (check-some-function 10 20))
>  (check (check-some-function 5 11) :on-fail (format t "UNEQ for 5 11~%")))
> 
> Check: Test Form (EQ (+ 1 2) 3) Passes
> Check: Test Form (CHECK-SOME-FUNCTION 2 4) Passes
> **ERROR** #<CONDITIONS:TYPE-COERCION-ERROR 2140A844>Check: Test Form (STRING-EQUAL "Test"
> 10) Fails
> Check: Test Form (CHECK-SOME-FUNCTION 10 20) Passes
> Check: Test Form (CHECK-SOME-FUNCTION 5 11) Fails
> UNEQ for 5 11
> 3 passes; 2 failures
> NIL
> 
> TESTING 49 >
> |#
From: Wade Humeniuk
Subject: Re: Critique my code?
Date: 
Message-ID: <aa5ep9$q8i$1@news3.cadvision.com>
"Peter Seibel" <·····@javamonkey.com> wrote in message
···················@javamonkey.com...
> "Wade Humeniuk" <········@cadvision.com> writes:
> Thanks. Wrapping things in handler-case is definitely a good idea and
> there are several other ideas there that I think I get the point of.
> I'm not sure I understand your use of the 'identity' function. How is
> ,(identity foo) different from just plain ,foo?
>

I am scratching my head over that right now.  I got into the habit of doing it because
somewhere I thought I got into trouble using just ,foo like syntax in past macros.  Right
now its just escaping me.  I think I will chalk that up to a faulty memory.  Change to
,foo instead.

Wade
From: Thomas F. Burdick
Subject: Re: Critique my code?
Date: 
Message-ID: <xcvofg8g5vm.fsf@famine.OCF.Berkeley.EDU>
"Wade Humeniuk" <········@cadvision.com> writes:

> "Peter Seibel" <·····@javamonkey.com> wrote in message
> ···················@javamonkey.com...
> > "Wade Humeniuk" <········@cadvision.com> writes:
> > Thanks. Wrapping things in handler-case is definitely a good idea and
> > there are several other ideas there that I think I get the point of.
> > I'm not sure I understand your use of the 'identity' function. How is
> > ,(identity foo) different from just plain ,foo?
> >
> 
> I am scratching my head over that right now.  I got into the habit of doing it because
> somewhere I thought I got into trouble using just ,foo like syntax in past macros.  Right
> now its just escaping me.  I think I will chalk that up to a faulty memory.  Change to
> ,foo instead.

Well, there are a couple places that you want to do ,(identity foo) :
inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
you're writing the macro, it should be painfully obvious when you're
in one of those (kind of weird) situations.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Erik Naggum
Subject: Re: Critique my code?
Date: 
Message-ID: <3228684634209403@naggum.net>
* Thomas F. Burdick
| Well, there are a couple places that you want to do ,(identity foo) :
| inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
| you're writing the macro, it should be painfully obvious when you're
| in one of those (kind of weird) situations.

  This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
  the same object as seen by the backquote expander.  In neither of the
  cases you mention would it make any sense at all to interpolate anything
  at a level where there would be a confusion.  I mean, (identity ,foo)
  would be suitable to protect a form used for side-effect at top-level
  within the tagbody, but ,(identity foo) would make no difference at all.

  I _really_ wonder how this "convention" evolved, and from what.

///
-- 
  In a fight against something, the fight has value, victory has none.
  In a fight for something, the fight is a loss, victory merely relief.

  Post with compassion: http://home.chello.no/~xyzzy/kitten.jpg
From: Thomas F. Burdick
Subject: Re: Critique my code?
Date: 
Message-ID: <xcvr8l4vb8o.fsf@conquest.OCF.Berkeley.EDU>
Erik Naggum <····@naggum.net> writes:

> * Thomas F. Burdick
> | Well, there are a couple places that you want to do ,(identity foo) :
> | inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
> | you're writing the macro, it should be painfully obvious when you're
> | in one of those (kind of weird) situations.
> 
>   This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
>   the same object as seen by the backquote expander.  In neither of the
>   cases you mention would it make any sense at all to interpolate anything
>   at a level where there would be a confusion.  I mean, (identity ,foo)
>   would be suitable to protect a form used for side-effect at top-level
>   within the tagbody, but ,(identity foo) would make no difference at all.
> 
>   I _really_ wonder how this "convention" evolved, and from what.

Yow, I read the post I responded to as (identity ,foo) and thought I'd
written the same.  I have no idea why anyone would write
,(identity foo).  The human capacity to see what one is expecting can
be amazing.

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Peter Seibel
Subject: Re: Critique my code?
Date: 
Message-ID: <e500013c.0204252043.5674fd87@posting.google.com>
Erik Naggum <····@naggum.net> wrote in message news:<················@naggum.net>...
> * Thomas F. Burdick
> | Well, there are a couple places that you want to do ,(identity foo) :
> | inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
> | you're writing the macro, it should be painfully obvious when you're
> | in one of those (kind of weird) situations.
> 
>   This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
>   the same object as seen by the backquote expander.  In neither of the
>   cases you mention would it make any sense at all to interpolate anything
>   at a level where there would be a confusion.  I mean, (identity ,foo)
>   would be suitable to protect a form used for side-effect at top-level
>   within the tagbody, but ,(identity foo) would make no difference at all.

Phew. I thought I was going to have to bang my head against the CLHS
for quite some time to figure out why ,(identity foo) was different
than ,foo. Thanks.

I am still having some trouble imagining how (identity ,foo) would
have a diferent effect in a macro expansion than just ,foo. (Other
than the obvious case where you're expanding it into some context
that, after the expansion, requires a function. But that wouldn't
really have anything to do with the macro expansion.) Can someone
post, for my edification, a short macro that uses (identity ,foo)
where ,foo wouldn't work?

-Peter

>   I _really_ wonder how this "convention" evolved, and from what.
> 
> ///
From: Wade Humeniuk
Subject: Re: Critique my code?
Date: 
Message-ID: <aabnnf$lgk$1@news3.cadvision.com>
"Peter Seibel" <·····@javamonkey.com> wrote in message
·································@posting.google.com...
> Erik Naggum <····@naggum.net> wrote in message news:<················@naggum.net>...
> > * Thomas F. Burdick
> > | Well, there are a couple places that you want to do ,(identity foo) :
> > | inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
> > | you're writing the macro, it should be painfully obvious when you're
> > | in one of those (kind of weird) situations.
> >
> >   This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
> >   the same object as seen by the backquote expander.  In neither of the
> >   cases you mention would it make any sense at all to interpolate anything
> >   at a level where there would be a confusion.  I mean, (identity ,foo)
> >   would be suitable to protect a form used for side-effect at top-level
> >   within the tagbody, but ,(identity foo) would make no difference at all.
>
> Phew. I thought I was going to have to bang my head against the CLHS
> for quite some time to figure out why ,(identity foo) was different
> than ,foo. Thanks.

Sorry about the confusion.  On reflection I have realized where I have picked up the
habit.  It was from my pre-CL days when I was using Scheme.  There was a defmacro in the
Scheme in I was using and ,foo did not behave like the CL defmacro.  I am not sure why it
did, perhaps it ran ,foo at the top-level and not within the function.  I guess it stung
me and I have only just recovered.

Wade
From: Peter Seibel
Subject: Re: Critique my code?
Date: 
Message-ID: <e500013c.0204261430.c9338cf@posting.google.com>
"Wade Humeniuk" <········@cadvision.com> wrote in message news:<············@news3.cadvision.com>...
> "Peter Seibel" <·····@javamonkey.com> wrote in message
> ·································@posting.google.com...
> > Erik Naggum <····@naggum.net> wrote in message news:<················@naggum.net>...
> > > * Thomas F. Burdick
> > > | Well, there are a couple places that you want to do ,(identity foo) :
> > > | inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
> > > | you're writing the macro, it should be painfully obvious when you're
> > > | in one of those (kind of weird) situations.
> > >
> > >   This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
> > >   the same object as seen by the backquote expander.  In neither of the
> > >   cases you mention would it make any sense at all to interpolate anything
> > >   at a level where there would be a confusion.  I mean, (identity ,foo)
> > >   would be suitable to protect a form used for side-effect at top-level
> > >   within the tagbody, but ,(identity foo) would make no difference at all.
> >
> > Phew. I thought I was going to have to bang my head against the CLHS
> > for quite some time to figure out why ,(identity foo) was different
> > than ,foo. Thanks.
> 
> Sorry about the confusion.  On reflection I have realized where I have picked up the
> habit.  It was from my pre-CL days when I was using Scheme.  There was a defmacro in the
> Scheme in I was using and ,foo did not behave like the CL defmacro.  I am not sure why it
> did, perhaps it ran ,foo at the top-level and not within the function.  I guess it stung
> me and I have only just recovered.
> 
> Wade


No worries. Anyway, it made me go look up the identity function and
learn about it which I might not have otherwise. ;-) And thanks for
your comments on my code.


-Peter
From: Thomas F. Burdick
Subject: Re: Critique my code?
Date: 
Message-ID: <xcv8z7a88jm.fsf@apocalypse.OCF.Berkeley.EDU>
·····@javamonkey.com (Peter Seibel) writes:

> Erik Naggum <····@naggum.net> wrote in message news:<················@naggum.net>...
> > * Thomas F. Burdick
> > | Well, there are a couple places that you want to do ,(identity foo) :
> > | inside TAGBODYs and LOOPs, for example.  Especially LOOP.  But when
> > | you're writing the macro, it should be painfully obvious when you're
> > | in one of those (kind of weird) situations.
> > 
> >   This is puzzling.  Both ,foo and ,(identity foo) would result in exactly
> >   the same object as seen by the backquote expander.  In neither of the
> >   cases you mention would it make any sense at all to interpolate anything
> >   at a level where there would be a confusion.  I mean, (identity ,foo)
> >   would be suitable to protect a form used for side-effect at top-level
> >   within the tagbody, but ,(identity foo) would make no difference at all.
> 
> Phew. I thought I was going to have to bang my head against the CLHS
> for quite some time to figure out why ,(identity foo) was different
> than ,foo. Thanks.
> 

> I am still having some trouble imagining how (identity ,foo) would
> have a diferent effect in a macro expansion than just ,foo.

No, you're not having any imagination troubles, you've got it right, I
think:

> (Other than the obvious case where you're expanding it into some
> context that, after the expansion, requires a function. But that
> wouldn't really have anything to do with the macro expansion.)

I *think* what you mean here is "other than the obvious case where
you're expanding it into some context that, after expansion, requires
a compound form."  It's not very profound, but it is a reason that one
would write `(... (identity ,foo) ...).

> Can someone post, for my edification, a short macro that uses
> (identity ,foo) where ,foo wouldn't work?

Uh, sure:

  (defmacro with-escape-hatch (hatch-name form)
      (let ((end (gensym))
            (loop (gensym)))
        `(symbol-macrolet ((,hatch-name (go ,end)))
          (tagbody
             ,loop
             (identity ,form)
             (go ,loop)
             ,end))))

  (defun collatz (n)
    (let (result)
      (with-escape-hatch eureka!
        (cond
          ((<= x 0) eureka!)
          (t (push n result)
             (if (evenp x)
                 (setf x (/ x 2))
                 (setf x (1+ (* x 3)))))))
      result))

(you said short, you didn't say useful)

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Paul F. Dietz
Subject: Re: Critique my code?
Date: 
Message-ID: <3CCA13D6.E43122BC@interaccess.com>
"Thomas F. Burdick" wrote:
> 
> ·····@javamonkey.com (Peter Seibel) writes:
 
> > Can someone post, for my edification, a short macro that uses
> > (identity ,foo) where ,foo wouldn't work?
> 
> Uh, sure:

  [ deleted ]

I also sometimes use identity to prevent a macro from
being expandable as a place (in a setf or the like).

(defmacro writable-foo (x) `(car ,x))
(defmacro read-only-foo (x) `(identity (car ,x)))
;; This is legal:
(setf (writable-foo z) 'bar)
;; This is not:
(setf (read-only-foo z) 'bar)

	Paul
From: Thomas A. Russ
Subject: Re: Critique my code?
Date: 
Message-ID: <ymi1yd5c52r.fsf@sevak.isi.edu>
Peter Seibel <·····@javamonkey.com> writes:


> Particular questions I have about this code are:
> 
>  - Is my use of special variables justified?

It would seem to be a perfect use of special variables.  Stylistically,
though, the general convention is to start and end special variable
names with '*'.  That would make them *passes* and *fails*

>  - Is this an appropriate use of macros?

Yes.

>  - Is there some simpler way to do what I'm doing in the line in
>  'check' that starts: '(let ((,bindings (list ...'?

Maybe.  I didn't spend enough time looking at the code to figure
out what you were trying to do with that code.  It did look a bit
tricky, though.  I looked at the macroexpansion and don't see
anything that would really be much simpler.

> (defpackage "TESTING"
>   (:use "COMMON-LISP")
>   (:export "RUN-TESTS" "CHECK"))
> 
> (in-package "TESTING")
> 
> (defmacro run-tests (&body body)
>   "Prepare to run a set of tests. Calls to the check macro must be
> wrapped, at some level, in a call to this macro. Prints the number of
> passes and failures and returns true if there were no failures."
>   `(let ((passes 0) (fails 0))
>      (declare (special passes fails))
>      ,@body
>      (format t "~D passes; ~D failures~%" passes fails)

Fancier:

       (format t "~D ·····@[es~*~]; ~D failure~:P"
                   passes (not (eql passes 1)) fails)

This uses the plural forming operator ~P, which has options for
adding a single "s" or either a "y" or "ies", but not an "es", which
is why a more complicated test form is needed for the first case.

>      (= fails 0)))
>   
> (defmacro check (&body forms)
>   "Evaluate a set of forms and report pass or fail depending on
> whether they evaluate to true (non nil) or false. Additionally you can
> include the forms (:show ...) and (:show-with ...) to control how the
> forms are displayed in the results. The items after :show should be
> symbols that you want to see the values of in the report. After
> :show-with you can list dotted-pairs consisting of symbols and the
> form that should be used to get the representation for that symbol."
>   (flet ((control-p (form) (keywordp (first form)))
> 	 (extract-bound-forms (control-items)
> 	   (loop for c in control-items
> 	     when (eq (first c) :show) append (mapcar (lambda (c) (cons c c)) (rest c))
> 	     when (eq (first c) :show-with) append (rest c))))
>     (let ((bindings (gensym))
> 	  (tests (remove-if #'control-p forms))
> 	  (bound-forms (extract-bound-forms (remove-if-not #'control-p forms))))
                                            ^^^^^^^^^^^^^^
This turns out to be superfluous (although cautious) since it happens
that the EXTRACT-BOUND-FORMS functions ignores anything that isn't a
control form anyway.

>       `(prog ()

PROGN is more normal, but wouldn't allow the declaration. However, since
you don't need the declaration at this level, you could just skip this
part of the form entirely.  Move the special declaration inside the FLET
definition for PASS-FAIL, which is the only place it is used.  Then you
could use the LET block as the returned value of the macro expansion.

> 	 (declare (special passes fails))
>          (let ((,bindings (list ,@(mapcar (lambda (symbol) `(cons ',symbol ,(cdr (assoc symbol bound-forms)))) (mapcar #'car bound-forms)))))
> 	   (flet ((pass-fail (result form)
> 		    (let ((label (if result "pass" "fail")))
>                       (format t "~A ... ~A~%" label form)
> 		      (if result (incf passes) (incf fails)))))
> 	     ,@(mapcar (lambda (test) `(pass-fail ,test (sublis ,bindings ',test))) tests)))))))
> 

The final result for the FLET clause can be simplified using fancier
format options as well:

    (flet ((pass-fail (result form))
           (declare (special *passes* *fails*))
           (format *trace-output* "~:[fail~;pass~]...~A~%" result form)
           (if result
              (incf *passes*)
              (incf *fails*)))
        ...)

I also chose to direct the output to *TRACE-OUTPUT* instead of to
*STANDARD-OUTPUT* (via T) since that seems to be the more specific
stream to use.  That also allows the test's summary output to be
redirected without affecting any normal output of the tested functions
(or vice versa).


> Peter Seibel
> ·····@javamonkey.com

-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu