From: Andreas Hinze
Subject: How to decline a condition ?
Date: 
Message-ID: <40b3994f$0$273$4d4ebb8e@read.news.de.uu.net>
Hi group,

i ran into trouble while trying to catch all conditions but 'UNDEFINED-FUNCTION.

When any kind of condition ocours a handler-case should return NIL but when there
is an condition of type 'UNDEFINED-FUNCTION then it should signal an error.
The only way that i found is (the code is obviously only for demonstration):

   (defun test-condition (body)
      (handler-case (eval body)
        (undefined-function (c) (error c))
        (condition () nil)))

   CL-USER 5 : 1 > (test-condition '(oddp '(3 4)))
   NIL

   CL-USER 6 : 1 > (test-condition '(foo '(3 4)))

   Error: Undefined function FOO called with arguments ((3 4)).
     1 (abort) Return to level 1.
     2 Return to debug level 1.
     3 Return to level 0.
     4 Return to top loop level 0.

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

So far so good. But in the CLHS section 9.1 i find:

"If a handler is invoked, it can address the situation in one of three ways:

  Decline

     It can decline to handle the condition. It does this by simply returning rather
     than transferring control. When this happens, any values returned by the handler
     are ignored and the next most recently established handler is invoked. If there
     is no such handler and the signaling function is error or cerror, the debugger is
     entered in the dynamic environment of the signaler. If there is no such handler and
     the signaling function is either signal or warn, the signaling function simply returns
     nil.

     ... "

This is more what i want to do. In my solution the handler in test-condition is
still active (isn't it ?) but not used anymore.

But with

   (defun test-condition2 (body)
    (handler-case (eval body)
      (undefined-function () (return-from test-condition2)) ; now let's return
      (condition () nil)))

   CL-USER 7 : 2 > (test-condition2 '(foo '(3 4)))
   NIL

And this looks not like that the debugger catches the condition :-(

So, whats wrong with my understanding of the condition system ?

Kind regards
AHz

From: Wade Humeniuk
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <NBNsc.8453$J02.2447@edtnps84>
Andreas Hinze wrote:

>   (defun test-condition2 (body)
>    (handler-case (eval body)
>      (undefined-function () (return-from test-condition2)) ; now let's 
> return
>      (condition () nil)))
> 
>   CL-USER 7 : 2 > (test-condition2 '(foo '(3 4)))
>   NIL
> 
> And this looks not like that the debugger catches the condition :-(

RETURN-FROM returns nil by default.  Your function is working, do

(defun test-condition2 (body)
    (handler-case (eval body)
      (undefined-function () (return-from test-condition2 10)) ; now let's return
      (condition () nil)))

CL-USER 7 > (test-condition2 '(foo 1 2))
10

CL-USER 8 >

Wade
From: Andreas Hinze
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <40b46317$0$267$4d4ebb8e@read.news.de.uu.net>
Wade Humeniuk wrote:
> 
> RETURN-FROM returns nil by default.  Your function is working, do
> 
> (defun test-condition2 (body)
>    (handler-case (eval body)
>      (undefined-function () (return-from test-condition2 10)) ; now 
> let's return
>      (condition () nil)))
> 
> CL-USER 7 > (test-condition2 '(foo 1 2))
> 10
> 
> CL-USER 8 >
> 
Hi,
maybe i was unclear at this point. I expected that my example decline
the condition (and yours too) since it simple returns as described in the CLHS.
But then the debugger - as the most "toplevel" handler - should catch the
condition. But this doesn't happen.
So now i wonder whats wrong with my understanding of the condition system.

Kind regards
AHz
From: Christophe Rhodes
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <sq65ajedim.fsf@cam.ac.uk>
Andreas Hinze <···@smi.de> writes:

> So now i wonder whats wrong with my understanding of the condition system.

HANDLER-CASE is not HANDLER-BIND.  If a handler-case clause matches,
the condition will be handled.  If a handler-bind clause matches, the
handler function decides whether or not to handle or decline to handle
the condition.

Loosely,
  (handler-case
      (form)
    (error () nil))
is equivalent to
  (block foo
    (handler-bind
        ((error (lambda (c) (declare (ignore c)) (return-from foo nil))))
      (form)))
but try MACROEXPAND on a handler-case to see some of the details I've
elided.

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Andreas Hinze
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <40b46d30$0$266$4d4ebb8e@read.news.de.uu.net>
Christophe Rhodes wrote:

> Andreas Hinze <···@smi.de> writes:
> 
> 
>>So now i wonder whats wrong with my understanding of the condition system.
> 
> 
> HANDLER-CASE is not HANDLER-BIND.  If a handler-case clause matches,
> the condition will be handled.  If a handler-bind clause matches, the
> handler function decides whether or not to handle or decline to handle
> the condition.
> 
> Loosely,
>   (handler-case
>       (form)
>     (error () nil))
> is equivalent to
>   (block foo
>     (handler-bind
>         ((error (lambda (c) (declare (ignore c)) (return-from foo nil))))
>       (form)))
> but try MACROEXPAND on a handler-case to see some of the details I've
> elided.
> 
Hi,
that's exactly what i was missing. Indeed, when handler-case expand to this
then it is obviously not possible to decline the condition since the return-from
prevent the local exit.

Thanks for the explanation.

Kind regards
AHz
From: Jacek Generowicz
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <tyfk6yriich.fsf@pcepsft001.cern.ch>
Andreas Hinze <···@smi.de> writes:

> Christophe Rhodes wrote:
> 
> > Andreas Hinze <···@smi.de> writes:
> >
> 
> >>So now i wonder whats wrong with my understanding of the condition system.
> > HANDLER-CASE is not HANDLER-BIND.  If a handler-case clause matches,
> > the condition will be handled.  If a handler-bind clause matches, the
> > handler function decides whether or not to handle or decline to handle
> > the condition.
> > Loosely,
> 
> >   (handler-case
> >       (form)
> >     (error () nil))
> > is equivalent to
> >   (block foo
> >     (handler-bind
> >         ((error (lambda (c) (declare (ignore c)) (return-from foo nil))))
> >       (form)))
> > but try MACROEXPAND on a handler-case to see some of the details I've
> > elided.
> >
> 
> Hi,
> that's exactly what i was missing. Indeed, when handler-case expand to this
> then it is obviously not possible to decline the condition since the return-from
> prevent the local exit.

Just in case it's useful to any future Googlers, when I was trying to
get my head around this a while ago, I wrote the code which I include
at the end of the message. It's essentially a couple of macros which
allow you to to nest handler/restart-case/bind in a succinct way, and
display a record of what happens to the stack as the condition system
gets to play with the structure you selected.

The interface to it all is the function FN, which accepts a sequence
of functions as arguments. FN sets up a call chain through those
functions: HC establishes a handler-case, HB establishes a
handler-bind, RC establishes a restart-case and RB establishes a
restart-bind. At the end of the chain pass a function which signals one
of the conditions DECLINE or HANDLE. The latter is always handled, the
former is always declined by the handlers established in HC and HB.

The essential difference between what handler-case (#'hc) and
handler-bind (#'hc) do in Andreas' situation, can be observed in the
following

[21] (fn #'hb #'fn #'(lambda () (stack er (signal 'decline))))

FN: (FN)
HB: (FN HB)
FN: (FN HB FN)
ER: (FN HB FN ER)
HA: (FN HB FN ER HA)
HA: (FN HB FN ER HA)
ER: (FN HB FN ER)
FN: (FN HB FN)
HB: (FN HB)
FN: (FN)
NIL
[22] (fn #'hc #'fn #'(lambda () (stack er (signal 'decline))))

FN: (FN)
HC: (FN HC)
FN: (FN HC FN)
ER: (FN HC FN ER)
HA: (FN HC HA)
HA: (FN HC HA)
HC: (FN HC)
FN: (FN)
NIL


You can see that the two are essentially identical until the condition
is signalled. In the handler-bind case, the handler (labelled HA) gets
to run without the stack unwinding, it declines and everything
continues as if no condition were signalled. In the handler-case case,
you can see that the stack gets unwound (ER and FN disappear) right
down to the level of the handler-case (HC) before the handler (HA)
gets to do anything.




Here's the code that sets it all up:


(defvar *stack-trace* ())

;; Automate stack modification recording and reporting
(defmacro stack (label &body body)
   `(let ((*stack-trace* (append *stack-trace* (list ',label))))
      (format t "~%~s: ~a" ',label *stack-trace*)
      ,@body
      (format t "~%~s: ~a" ',label *stack-trace*)))

;; Assume that each function binds a variable called call-next
;; containing the next functions to call
(defmacro call-next ()
   '(when call-chain
      (apply (first call-chain) (rest call-chain))))

;;; Make all stack-trace symbols 2 letters long for uniformity of output.

;; The general-purpose function
(defun fn (&rest call-chain)
   (stack fn
     (call-next)))

(define-condition handle () ())   ; Handler always explicitly handles this.
(define-condition decline () ())  ; Handler always explicitly declines to handle this.

;;; Record where and how the handlers and restarts are established, in the stack.
;;; Record where the handlers and restarts get to work, in the stack.
(defun hc (&rest call-chain)
   (stack hc
     (handler-case
       (call-next)
       (handle () (stack ha (invoke-restart 'the-restart)))
       (decline () (stack ha)))))

(defun hb (&rest call-chain)
   (stack hb
     (handler-bind
       ((handle #'(lambda (c)
                    (declare (ignore c))
                    (stack ha
                      (invoke-restart 'the-restart))))
        (decline #'(lambda (c)
                     (declare (ignore c))
                     (stack ha))))
       (call-next))))

(defun rc (&rest call-chain)
   (stack rc
     (restart-case
       (call-next)
       (the-restart () (stack re)))))

(defun rb (&rest call-chain)
   (stack rb
     (restart-bind
       ((the-restart
         #'(lambda () (stack re))))
       (call-next))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Some calls to try:

;; Error: invokes unwound restart
(fn #'hc #'rc #'fn #'(lambda () (stack er (signal 'handle))))
;; Error: invokes unwound restart
(fn #'hc #'rb #'fn #'(lambda () (stack er (signal 'handle))))
;; Stack unwound before restart runs
(fn #'hb #'rc #'fn #'(lambda () (stack er (signal 'handle))))
;; Although handler handles, the restart it invokes fails to do a non-local transfer
;; of control, and as it was established by a restart-bind rather than restart-case,
;; there is no implicit transfer, so the condition is effectively NOT handled,
;; and we carry on as if nothing happened. No unwinding at all.
(fn #'hb #'rb #'fn #'(lambda () (stack er (signal 'handle))))

;; Handler-case implicitly handles: stack unwound before handler runs
(fn #'hc #'rc #'fn #'(lambda () (stack er (signal 'decline))))
;; Handler-case implicitly handles: stack unwound before handler runs
(fn #'hc #'rb #'fn #'(lambda () (stack er (signal 'decline))))
;; Signal not handled - carry on as if nothing happened
(fn #'hb #'rc #'fn #'(lambda () (stack er (signal 'decline))))
;; Signal not handled - carry on as if nothing happened
(fn #'hb #'rb #'fn #'(lambda () (stack er (signal 'decline))))

;; Error: invokes unwound restart
(fn #'hc #'rc #'fn #'(lambda () (stack er (error 'handle))))
;; Error: invokes unwound restart
(fn #'hc #'rb #'fn #'(lambda () (stack er (error 'handle))))
;; Stack unwound before restart runs, but not before handler
(fn #'hb #'rc #'fn #'(lambda () (stack er (error 'handle))))
;; Although handler handles, the restart it invokes fails to do a non-local transfer
;; of control, and as it was established by a restart-bind rather than restart-case,
;; there is no implicit transfer, so the condition is effectively NOT handled,
;; and we end up in the debugger. No unwinding at all.
(fn #'hb #'rb #'fn #'(lambda () (stack er (error 'handle))))

;; Handler-case implicitly handles: stack unwound before handler runs
(fn #'hc #'rc #'fn #'(lambda () (stack er (error 'decline))))
;; Handler-case implicitly handles: stack unwound before handler runs
(fn #'hc #'rb #'fn #'(lambda () (stack er (error 'decline))))
;; Declined, so we end up in the debugger
(fn #'hb #'rc #'fn #'(lambda () (stack er (error 'decline))))
;; Declined, so we end up in the debugger
(fn #'hb #'rb #'fn #'(lambda () (stack er (error 'decline))))
From: Arthur Lemmens
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <opr8lpossqk6vmsw@news.xs4all.nl>
Andreas Hinze wrote:

> Wade Humeniuk wrote:
>>
>> (defun test-condition2 (body)
>> (handler-case (eval body)
>> (undefined-function () (return-from test-condition2 10)) ; now let's return
>> (condition () nil)))
>>
>> CL-USER 7 > (test-condition2 '(foo 1 2))
>> 10
>
> maybe i was unclear at this point. I expected that my example decline
> the condition (and yours too) since it simple returns as described in the CLHS.
> But then the debugger - as the most "toplevel" handler - should catch the
> condition. But this doesn't happen.
> So now i wonder whats wrong with my understanding of the condition system.

Your HANDLER-CASE has already handled the condition. If you want
to pass some conditions (e.g. all errors except UNDEFINED-FUNCTION) to
a surrounding handler, you have to do this explicitly. For example:

(defun test-condition2 (body)
   (handler-case (eval body)
      (undefined-function () (return-from test-condition2 10))
      ;; Don't you love multiple namespaces?
      (error (error) (error error))))

I hope this helps.

Regards,

Arthur Lemmens
From: Andreas Hinze
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <40b46dd1$0$266$4d4ebb8e@read.news.de.uu.net>
Arthur Lemmens wrote:

> 
> Your HANDLER-CASE has already handled the condition. 

> 
> I hope this helps.
> 
Yes. After yours and Christophe's explanations i think i got it
finally.

Thanks again to all who replied.

Kind regards
AHz
From: Frode Vatvedt Fjeld
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <2h7jv0s1re.fsf@vserver.cs.uit.no>
Andreas Hinze <···@smi.de> writes:

> i ran into trouble while trying to catch all conditions but
> 'UNDEFINED-FUNCTION.

This is quite easily expressed like so:

  (defun test-condition (form)
    (handler-case (eval form)
      ((not undefined-function)
       nil)))

> [..] "If a handler is invoked, it can address the situation in one
> of three ways: [..]

Note that "handlers" here are the thingies you install with
_handler-bind_, not the clauses to handler-case, which are quite
different. Understanding the difference between handler-bind and
handler-case is key to understanding CL's condition system.

-- 
Frode Vatvedt Fjeld
From: Andreas Hinze
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <40b46755$0$261$4d4ebb8e@read.news.de.uu.net>
Frode Vatvedt Fjeld wrote:

> Andreas Hinze <···@smi.de> writes:
> 
> 
>>i ran into trouble while trying to catch all conditions but
>>'UNDEFINED-FUNCTION.
> 
> 
> This is quite easily expressed like so:
> 
>   (defun test-condition (form)
>     (handler-case (eval form)
>       ((not undefined-function)
>        nil)))
> 
Excellent idea. I'll never had found this solution by myself but it is
quite obvious when i read it. Maybe i fooled myself because i assumed
that handler-case works like case but it is more like typecase.
Thanks for the hint.


Kind regards
AHz
From: Harald Hanche-Olsen
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <pco7jv0i6el.fsf@shuttle.math.ntnu.no>
+ Andreas Hinze <···@smi.de>:

| When any kind of condition ocours a handler-case should return NIL
| but when there is an condition of type 'UNDEFINED-FUNCTION then it
| should signal an error.  The only way that i found is (the code is
| obviously only for demonstration):
| 
|    (defun test-condition (body)
|       (handler-case (eval body)
|         (undefined-function (c) (error c))
|         (condition () nil)))
[...]
| So far so good. But in the CLHS section 9.1 i find:
| 
| "If a handler is invoked, it can address the situation in one of three ways:
| 
|   Decline
|      ... "
| 
| This is more what i want to do.

Ah, but that description is more appropriate to HANDLER-BIND than to
HANDLER-CASE.  The latter is an abstraction built on the former.  Look
under Notes at the end of the description of HANDLER-CASE to see what
I mean.

| But with
| 
|    (defun test-condition2 (body)
|     (handler-case (eval body)
|       (undefined-function () (return-from test-condition2)) ; now let's return
|       (condition () nil)))
| 
|    CL-USER 7 : 2 > (test-condition2 '(foo '(3 4)))
|    NIL
| 
| And this looks not like that the debugger catches the condition :-(

No, because the RETURN-FROM is a non-local exit from the handler,
which means that the handler has handled the condition, according to
9.1.

| So, whats wrong with my understanding of the condition system ?

Maybe that HANDLER-CASE arranges for a non-local exit when one of the
cases returns a value?

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Andreas Hinze
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <40b46acb$0$266$4d4ebb8e@read.news.de.uu.net>
Harald Hanche-Olsen wrote:

> 
> | But with
> | 
> |    (defun test-condition2 (body)
> |     (handler-case (eval body)
> |       (undefined-function () (return-from test-condition2)) ; now let's return
> |       (condition () nil)))
> | 
> |    CL-USER 7 : 2 > (test-condition2 '(foo '(3 4)))
> |    NIL
> | 
> | And this looks not like that the debugger catches the condition :-(
> 
> No, because the RETURN-FROM is a non-local exit from the handler,
> which means that the handler has handled the condition, according to
> 9.1.
> 
> | So, whats wrong with my understanding of the condition system ?
> 
> Maybe that HANDLER-CASE arranges for a non-local exit when one of the
> cases returns a value?
> 
Hi,
althrough Frode Vatvedt Fjeld shows me a solution for my primary problem
i still doesn't understand how to decline the condition.
You are right. RETURN-FROM is a non-local exit and then the condition is
assumed to be handled.
But a local exit (i.e. with (values) ) also doesn't raise the debugger
(which i expect to be the outermost condition handler):

    (defun test-condition2 (body)
        (handler-case (eval body)
          (undefined-function () (values)) ; this should be a local-exit without return-values
          (condition () nil)))

   CL-USER 8 : 1 > test-condition2 '(foo '(2 3))

   CL-USER 9 : 1 >

And if i return a value the result is similar, that is the result of the call
is the values returned.

Seems that i don't understand it :-(

Kind regards
AHz
From: Barry Margolin
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <barmar-474524.08302826052004@comcast.dca.giganews.com>
In article <·······················@read.news.de.uu.net>,
 Andreas Hinze <···@smi.de> wrote:

> Harald Hanche-Olsen wrote:
> 
> > 
> > | But with
> > | 
> > |    (defun test-condition2 (body)
> > |     (handler-case (eval body)
> > |       (undefined-function () (return-from test-condition2)) ; now let's 
> > return
> > |       (condition () nil)))
> > | 
> > |    CL-USER 7 : 2 > (test-condition2 '(foo '(3 4)))
> > |    NIL
> > | 
> > | And this looks not like that the debugger catches the condition :-(
> > 
> > No, because the RETURN-FROM is a non-local exit from the handler,
> > which means that the handler has handled the condition, according to
> > 9.1.
> > 
> > | So, whats wrong with my understanding of the condition system ?
> > 
> > Maybe that HANDLER-CASE arranges for a non-local exit when one of the
> > cases returns a value?
> > 
> Hi,
> althrough Frode Vatvedt Fjeld shows me a solution for my primary problem
> i still doesn't understand how to decline the condition.

You can *only* do that with HANDLER-BIND.

> You are right. RETURN-FROM is a non-local exit and then the condition is
> assumed to be handled.

Not in this case.  The non-local exit that really matters is in the 
expansion of HANDLER-CASE -- it performs a non-local exit to the code 
that contains the case body.

> But a local exit (i.e. with (values) ) also doesn't raise the debugger
> (which i expect to be the outermost condition handler):
> 
>     (defun test-condition2 (body)
>         (handler-case (eval body)
>           (undefined-function () (values)) ; this should be a local-exit 
>           without return-values
>           (condition () nil)))
> 
>    CL-USER 8 : 1 > test-condition2 '(foo '(2 3))
> 
>    CL-USER 9 : 1 >
> 
> And if i return a value the result is similar, that is the result of the call
> is the values returned.
> 
> Seems that i don't understand it :-(

You've already handled the error and exited by the time that (values) 
form is executed.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Thomas A. Russ
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <ymiu0y1reuq.fsf@sevak.isi.edu>
Hmm.  I only seemed to get some of the follow-ups on your question, but
since I didn't see it, here is my input:

You need to look at HANDLER-BIND, which uses the actual handlers that
have the option of declining to handle a particular condition.  This is
a slightly lower-level construct than HANDLER-CASE, but it should let
you do what you want.

As far as I'm concerned, resignaling via ERROR should also work (unless
it messes up the stack trace information.)

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Rahul Jain
Subject: Re: How to decline a condition ?
Date: 
Message-ID: <877juqpl9j.fsf@nyct.net>
···@sevak.isi.edu (Thomas A. Russ) writes:

> As far as I'm concerned, resignaling via ERROR should also work (unless
> it messes up the stack trace information.)

It's not stack trace information, but the availability of restarts
within the HANDLER-CASE's form. You've just blown away the (inside part
of the) stack itself, not just the stack trace that gets printed in the
debugger.

-- 
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist