From: Tamas Papp
Subject: catching multiple values
Date: 
Message-ID: <87ps52rkq8.fsf@pu100877.student.princeton.edu>
Hi,

I am writing a wrapper function to C code (still cairo ;-), where
error status needs to be queried explicitly.  The following
self-contained code demonstrates what I am trying to do:

(defun query-status () 'success)

(defmacro catch-error (&body body)
  (let ((result (gensym))
	(status (gensym)))
    `(let* ((,result (progn ,@body))
	    (,status (query-status)))
       (if (eq ,status 'success)
	   ,result
	   (warn "status is ~a" ,status)))))

Now the fly in the ointment: it works fine if body returns a single value:

(catch-error
  (+ 1 1))

but not so with multiple values:

(catch-error
  (floor pi))

Is there a solution to this that would work in both cases?

Thanks,

Tamas

-- 
Posted via a free Usenet account from http://www.teranews.com

From: Geoffrey Summerhayes
Subject: Re: catching multiple values
Date: 
Message-ID: <1179341140.177805.314700@h2g2000hsg.googlegroups.com>
On May 15, 2:43 pm, Tamas Papp <······@gmail.com> wrote:
> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly.  The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
>   (let ((result (gensym))
>         (status (gensym)))
>     `(let* ((,result (progn ,@body))
>             (,status (query-status)))
>        (if (eq ,status 'success)
>            ,result
>            (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:
>
> (catch-error
>   (+ 1 1))
>
> but not so with multiple values:
>
> (catch-error
>   (floor pi))
>
> Is there a solution to this that would work in both cases?

(defmacro catch-error (&body body)
  (let ((result (gensym))
        (status (gensym)))
    `(let* ((,result (multiple-value-list (progn ,@body)))
            (,status (query-status)))
       (if (eq ,status 'success)
           (apply #'values ,result)
           (warn "status is ~a" ,status)))))

---
Geoff
From: Geoffrey Summerhayes
Subject: Re: catching multiple values
Date: 
Message-ID: <1179341250.953146.321010@e65g2000hsc.googlegroups.com>
On May 15, 2:43 pm, Tamas Papp <······@gmail.com> wrote:
> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly.  The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
>   (let ((result (gensym))
>         (status (gensym)))
>     `(let* ((,result (progn ,@body))
>             (,status (query-status)))
>        (if (eq ,status 'success)
>            ,result
>            (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:
>
> (catch-error
>   (+ 1 1))
>
> but not so with multiple values:
>
> (catch-error
>   (floor pi))
>
> Is there a solution to this that would work in both cases?

(defmacro catch-error (&body body)
  (let ((result (gensym))
        (status (gensym)))
    `(let* ((,result (multiple-value-list (progn ,@body)))
            (,status (query-status)))
       (if (eq ,status 'success)
           (apply #'values ,result)
           (warn "status is ~a" ,status)))))
---
Geoff
From: Kaz Kylheku
Subject: Re: catching multiple values
Date: 
Message-ID: <1179342646.694598.199370@y80g2000hsf.googlegroups.com>
On May 15, 11:43 am, Tamas Papp <······@gmail.com> wrote:
> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly.  The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
>   (let ((result (gensym))
>         (status (gensym)))
>     `(let* ((,result (progn ,@body))
>             (,status (query-status)))
>        (if (eq ,status 'success)
>            ,result
>            (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:

The easiest way to fix this is to capture the multiple values out of
the BODY as a list using MULTIPLE-VALUE-LIST. The list is then be
converted back to multiple values using VALUES-LIST.
From: ········@gmail.com
Subject: Re: catching multiple values
Date: 
Message-ID: <1179345093.770212.267540@h2g2000hsg.googlegroups.com>
On May 15, 11:43 am, Tamas Papp <······@gmail.com> wrote:
> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly.  The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
>   (let ((result (gensym))
>         (status (gensym)))
>     `(let* ((,result (progn ,@body))
>             (,status (query-status)))
>        (if (eq ,status 'success)
>            ,result
>            (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:
>
> (catch-error
>   (+ 1 1))
>
> but not so with multiple values:
>
> (catch-error
>   (floor pi))
>
> Is there a solution to this that would work in both cases?

if you know the number of values returned you can use MULTIPLE-VALUE-
BIND, otherwise you can use MULTIPLE-VALUES-LIST

(defmacro catch-error (&body body)
   (let ((result (gensym))
         (status (gensym)))
     `(let* ((,result (multiple-value-list (progn ,@body)))
             (,status (query-status)))
        (if (eq ,status 'success)
            (apply #'values ,result)
            (warn "status is ~a" ,status)))))
From: Kent M Pitman
Subject: Re: catching multiple values
Date: 
Message-ID: <u7ir8tgm4.fsf@nhplace.com>
········@gmail.com writes:

> On May 15, 11:43 am, Tamas Papp <······@gmail.com> wrote:
> > Hi,
> >
> > I am writing a wrapper function to C code (still cairo ;-), where
> > error status needs to be queried explicitly.  The following
> > self-contained code demonstrates what I am trying to do:
> >
> > (defun query-status () 'success)
> >
> > (defmacro catch-error (&body body)
> >   (let ((result (gensym))
> >         (status (gensym)))
> >     `(let* ((,result (progn ,@body))
> >             (,status (query-status)))
> >        (if (eq ,status 'success)
> >            ,result
> >            (warn "status is ~a" ,status)))))
...
> if you know the number of values returned you can use MULTIPLE-VALUE-
> BIND, otherwise you can use MULTIPLE-VALUES-LIST
> 
> (defmacro catch-error (&body body)
>    (let ((result (gensym))
>          (status (gensym)))
>      `(let* ((,result (multiple-value-list (progn ,@body)))
>              (,status (query-status)))
>         (if (eq ,status 'success)
>             (apply #'values ,result)
>             (warn "status is ~a" ,status)))))

First, do you really just want the result of the call to WARN (that
is, NIL) to be re returned in the warning case?  It's usually clearer
to do that explicitly rather than just assume that an operator will
return the right thing.

Second, I'm not sure I see where the "catching" is going on.  I think you
should not call this macro catch-xxx unless it catches something.  If
instead what it is just passively review error status of normally
returning things and not catch other things, then maybe another name?
NOTICING-UNSUCCESSFUL-RESULT or WARNING-UPON-SUSPICIOUS-RETURN or some
such thing would work better for me.  Don't use a name with a meaning
already associated with it unless you mean to attach that meaning.

Third, just for grins, I want to point out that there are sometimes 
alternatives to multiple-value-list.  I've incorporated one of them here...

(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym)))
    `(block ,bypass
       (multiple-value-prog1 (progn ,@body)
         (unless (eq (query-status) 'success)
           (warn "status is ~A" ,status)
           (return-from ,bypass nil))))))

Or else perhaps you want to return the warning...

(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym "BYPASS")) (status (gensym "STATUS")))
    `(block ,bypass
       (multiple-value-prog1 (progn ,@body)
         (let ((,status (query-status)))
           (unless (eq ,status 'success)
             (return-from ,bypass (warn-about-suspicious-return ,status))))))))

(defun warn-about-suspicious-return (status)
  (let ((warning (make-instance 'simple-warning 
                                :format-control "status is ~A"
                                :format-arguments (list status))))
    (warn warning)
    warning))

I didn't test this code, so there might be errors, but hopefully you get
the point.
From: viper-2
Subject: Re: catching multiple values
Date: 
Message-ID: <1179939095.081340.173630@u30g2000hsc.googlegroups.com>
On May 16, 8:53 pm, Kent M Pitman <······@nhplace.com> wrote:

> Third, just for grins, I want to point out that there are sometimes
> alternatives to multiple-value-list.  I've incorporated one of them here...
>
> (defmacro warning-upon-suspicious-return (&body body)
>   (let ((bypass (gensym)))
>     `(block ,bypass
>        (multiple-value-prog1 (progn ,@body)
>          (unless (eq (query-status) 'success)
>            (warn "status is ~A" ,status)
>            (return-from ,bypass nil))))))
>
> Or else perhaps you want to return the warning...
>
> (defmacro warning-upon-suspicious-return (&body body)
>   (let ((bypass (gensym "BYPASS")) (status (gensym "STATUS")))
>     `(block ,bypass
>        (multiple-value-prog1 (progn ,@body)
>          (let ((,status (query-status)))
>            (unless (eq ,status 'success)
>              (return-from ,bypass (warn-about-suspicious-return ,status))))))))
>
> (defun warn-about-suspicious-return (status)
>   (let ((warning (make-instance 'simple-warning
>                                 :format-control "status is ~A"
>                                 :format-arguments (list status))))
>     (warn warning)
>     warning))
>
> I didn't test this code, so there might be errors, but hopefully you get
> the point.


My code is catching multiple worthless trolls :-)

I decided to test Kent Pitman's code since he said it hadn't been
tested. I used Pascal Bourguignon's procedure QUERY-STATUS:

The first procedure was fine with a minor edit to include a second LET
to bind the variable STATUS to the value returned by QUERY-STATUS. The
second procedure, however, consistently gave the error:

*** - EVAL: variable TROL has no value

Eventually, after replacing WARN-ABOUT-SUSPICIOUS-RETURN and WARNING
with shorter names, TEST-WARNING  and WRN, the code worked. On
examining the dribble file I noted that the arguments FORMAT-CONTROL
and  FORMAT-ARGUMENTS  were being truncated to TROL AND TS
respectively.

I'm not aware of a problem with reasonable name lengths. Here is the
edited dribble file evaluated using CLisp in Emacs Inferior Lisp Mode.
Can you tell what's going on?

[9]>
(let ((s t))
  (defun query-status ()
    (or (setf s (not s))
	'success)))
QUERY-STATUS

[10]>
(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym))
(status (gensym)))
    `(block ,bypass
      (let ((,status (query-status)))
	(multiple-value-prog1 (progn ,@body)
	  (unless (eq ,status 'success)
	    (warn "status is ~A" ,status)
	    (return-from ,bypass nil)))))))
WARNING-UPON-SUSPICIOUS-RETURN

[11]>
(warning-upon-suspicious-return (values 1 2 3))
1 ;
2 ;
3

[12]>
(warning-upon-suspicious-return (values 1 2 3))

WARNING:
status is T
NIL

[13]>
(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym "BYPASS"))
	(status (gensym "STATUS")))
    `(block ,bypass
      (multiple-value-prog1 (progn ,@body)
	(let ((,status (query-status)))
	  (unless (eq ,status 'success)
	    (return-from ,bypass (warn-about-suspicious-
return ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN

[14]>
(defun warn-about-suspicious-return (status)
  (let ((warning (make-instance 'simple-warning
				trol "status is ~A"
				ts (list status))))
    (warn warning)
    warning))
WARN-ABOUT-SUSPICIOUS-RETURN

[15]>
(warning-upon-suspicious-return (values  1 2 3))
1 ;
2 ;
3

[16]>
(warning-upon-suspicious-return (values  1 2 3))

*** - EVAL: variable TROL has no value

1. Break [17]>
(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym "BYPASS"))
	(status (gensym "STATUS")))
    `(block ,bypass
      (multiple-value-prog1 (progn ,@body)
	(let ((,status (query-status)))
	  (unless (eq ,status 'success)
	    (return-from ,bypass (warn-about-suspicious-
return ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN

1. Break [17]>
(defun warn-about-suspicious-return (status)
  (let ((wrn (make-instance 'simple-warning
			    trol "status is ~A"
			    ts (list status))))
    (warn wrn)
    wrn))
WARN-ABOUT-SUSPICIOUS-RETURN

1. Break [17]>
(warning-upon-suspicious-return (values  1 2 3))
1 ;
2 ;
3

1. Break [17]>
(warning-upon-suspicious-return (values  1 2 3))

*** - EVAL: variable TROL has no value

2. Break [18]>
(defmacro warning-upon-suspicious-return (&body body)
  (let ((bypass (gensym "BYPASS"))
	(status (gensym "STATUS")))
    `(block ,bypass
      (multiple-value-prog1 (progn ,@body)
	(let ((,status (query-status)))
	  (unless (eq ,status 'success)
	    (return-from ,bypass (test-warning ,status))))))))
WARNING-UPON-SUSPICIOUS-RETURN

2. Break [18]>
(defun test-warning (status)
  (let ((warning (make-instance 'simple-warning
				trol "status is ~A"
				ts (list status))))
    (warn warning)
    warning))
TEST-WARNING

2. Break [18]>
(warning-upon-suspicious-return (values  1 2 3))
1 ;
2 ;
3

2. Break [18]>
(warning-upon-suspicious-return (values  1 2 3))

*** - EVAL: variable TROL has no value

3. Break [19]>
(defun test-warning (status)
  (let ((wnr (make-instance 'simple-warning
			    :format-control "status is ~A"
			    :format-arguments (list status))))
    (warn wnr)
    wnr))
TEST-WARNING

3. Break [19]>
(warning-upon-suspicious-return (values  1 2 3))
1 ;
2 ;
3

3. Break [19]>
(warning-upon-suspicious-return (values  1 2 3))

WARNING:
status is T
#<SIMPLE-WARNING #x203BE529>

3. Break [19]>
From: Pascal Bourguignon
Subject: Re: catching multiple values
Date: 
Message-ID: <87veesplmz.fsf@thalassa.lan.informatimago.com>
Tamas Papp <······@gmail.com> writes:

> Hi,
>
> I am writing a wrapper function to C code (still cairo ;-), where
> error status needs to be queried explicitly.  The following
> self-contained code demonstrates what I am trying to do:
>
> (defun query-status () 'success)
>
> (defmacro catch-error (&body body)
>   (let ((result (gensym))
> 	(status (gensym)))
>     `(let* ((,result (progn ,@body))
> 	    (,status (query-status)))
>        (if (eq ,status 'success)
> 	   ,result
> 	   (warn "status is ~a" ,status)))))
>
> Now the fly in the ointment: it works fine if body returns a single value:
>
> (catch-error
>   (+ 1 1))
>
> but not so with multiple values:
>
> (catch-error
>   (floor pi))
>
> Is there a solution to this that would work in both cases?

(defmacro catch-error (&body body)
   (let ((vstatus (gensym)))
      `(multiple-value-prog1 (progn ,@body)
          (let ((,vstatus (query-status)))
            (unless (eq ,vstatus 'success)
              (warn "status is ~A" ,vstatus))))))


C/USER[21]> (let ((s t)) (defun query-status () (or (setf s (not s)) 'success)))
QUERY-STATUS
C/USER[22]> (catch-error (values 1 2 3))
1 ;
2 ;
3
C/USER[23]> (catch-error (values 1 2 3))
WARNING: status is T
1 ;
2 ;
3

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

NOTE: The most fundamental particles in this product are held
together by a "gluing" force about which little is currently known
and whose adhesive power can therefore not be permanently
guaranteed.
From: Tamas Papp
Subject: Re: catching multiple values
Date: 
Message-ID: <87irassdef.fsf@pu100877.student.princeton.edu>
Pascal Bourguignon <···@informatimago.com> writes:

> (defmacro catch-error (&body body)
>    (let ((vstatus (gensym)))
>       `(multiple-value-prog1 (progn ,@body)
>           (let ((,vstatus (query-status)))
>             (unless (eq ,vstatus 'success)
>               (warn "status is ~A" ,vstatus))))))

This is very neat.  Thanks for all the replies.

Tamas
From: Kent M Pitman
Subject: Re: catching multiple values
Date: 
Message-ID: <u3b1wtgge.fsf@nhplace.com>
Pascal Bourguignon <···@informatimago.com> writes:

> (defmacro catch-error (&body body)
>    (let ((vstatus (gensym)))
>       `(multiple-value-prog1 (progn ,@body)
>           (let ((,vstatus (query-status)))
>             (unless (eq ,vstatus 'success)
>               (warn "status is ~A" ,vstatus))))))

Wow--deja vu.. I had to look twice to make sure I wasn't re-reading
the message I just sent.  I guess I should have waited before
replying!  (But I'm always afraid I won't be able to find the messages
again that I want to respond to, so I usually just jot something down
while I'm there.)