From: Janis Dzerins
Subject: Q: handler-case and restart-case
Date: 
Message-ID: <877l51wond.fsf@asaka.latnet.lv>
Hello.

I have problems using handler-case and restart-case together and would
be very glad if someone explained them.

Here are two functions:

(defun f-1 ()
  (handler-case
      (f-2)
    (t (condition)
      "Error: ~S" condition)))

(defun f-2 ()
  (loop with foo = t
      while foo
      do (restart-case
             (error "I don't like value of FOO: ~S" foo)
           (change-foo (new-foo)
             :report "Set FOO"
             :interactive (lambda ()
                            (format t "Enter new value: ")
                            (multiple-value-list (eval (read))))
             (setf foo new-foo)))))

Now some interaction (ACL 6.0 Trial):

cl-user(1): (trace f-2 f-1)
(f-1 f-2)
cl-user(2): (f-2)
 0: (f-2)
Error: I don't like value of FOO: t
  [condition type: simple-error]

Restart actions (select using :continue):
 0: Set FOO
 1: Return to Top Level (an "abort" restart)
 2: Abort #<process Initial Lisp Listener>
[1] cl-user(3): :cont 0
Enter new value: nil
 0: returned nil
nil
cl-user(4): (f-1)
 0: (f-1)
   1: (f-2)
   1: returned-by-throwing 0
 0: returned #<simple-error @ #x204621aa>
#<simple-error @ #x204621aa>
cl-user(5): 

What I don't understand is -- why RESTART-CASE doesn't work if F-2 is
called from F-1, but works if F-2 is called separately?

Janis Dzerins
-- 
  If million people say a stupid thing it's still a stupid thing.

From: Barry Margolin
Subject: Re: Q: handler-case and restart-case
Date: 
Message-ID: <y1u_5.11$As4.454@burlma1-snr2>
In article <··············@asaka.latnet.lv>,
Janis Dzerins  <·····@latnet.lv> wrote:
>Hello.
>
>I have problems using handler-case and restart-case together and would
>be very glad if someone explained them.
>
>Here are two functions:
>
>(defun f-1 ()
>  (handler-case
>      (f-2)
>    (t (condition)
>      "Error: ~S" condition)))
>
>(defun f-2 ()
>  (loop with foo = t
>      while foo
>      do (restart-case
>             (error "I don't like value of FOO: ~S" foo)
>           (change-foo (new-foo)
>             :report "Set FOO"
>             :interactive (lambda ()
>                            (format t "Enter new value: ")
>                            (multiple-value-list (eval (read))))
>             (setf foo new-foo)))))
>
>Now some interaction (ACL 6.0 Trial):
>
>cl-user(1): (trace f-2 f-1)
>(f-1 f-2)
>cl-user(2): (f-2)
> 0: (f-2)
>Error: I don't like value of FOO: t
>  [condition type: simple-error]
>
>Restart actions (select using :continue):
> 0: Set FOO
> 1: Return to Top Level (an "abort" restart)
> 2: Abort #<process Initial Lisp Listener>
>[1] cl-user(3): :cont 0
>Enter new value: nil
> 0: returned nil
>nil
>cl-user(4): (f-1)
> 0: (f-1)
>   1: (f-2)
>   1: returned-by-throwing 0
> 0: returned #<simple-error @ #x204621aa>
>#<simple-error @ #x204621aa>
>cl-user(5): 
>
>What I don't understand is -- why RESTART-CASE doesn't work if F-2 is
>called from F-1, but works if F-2 is called separately?

I'm not sure what you mean by "RESTART-CASE doesn't work".  Since you have
a handler for the condition, and it simply returns rather than going into
the debugg, so there's no opportunity to use *any* restarts -- not the
system's default restarts nor the ones you define.

However, I think you meant to write (error "Error: ~S" condition) in F-1.
In this case, you would see the system's restarts but not your own.  When a
condition is handled by HANDLER-CASE, the stack is unwound, so restarts
that were bound within the body of the HANDLER-CASE are no longer in
effect.  You would have to use HANDLER-BIND if you wanted to see the
restarts that are bound in the dynamic environment of the original error.

-- 
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: Janis Dzerins
Subject: Re: Q: handler-case and restart-case
Date: 
Message-ID: <873dflx4d9.fsf@asaka.latnet.lv>
Barry Margolin <······@genuity.net> writes:

> I'm not sure what you mean by "RESTART-CASE doesn't work".  Since you have
> a handler for the condition, and it simply returns rather than going into
> the debugg, so there's no opportunity to use *any* restarts -- not the
> system's default restarts nor the ones you define.

That's the problem I had -- F-2 would behave differently if called
from a function with or without HANDLER-CASE.

> However, I think you meant to write (error "Error: ~S" condition) in F-1.

Yes.

> In this case, you would see the system's restarts but not your own.  When a
> condition is handled by HANDLER-CASE, the stack is unwound, so restarts
> that were bound within the body of the HANDLER-CASE are no longer in
> effect.  You would have to use HANDLER-BIND if you wanted to see the
> restarts that are bound in the dynamic environment of the original error.

Thanks for insight and let me state what I wanted to achieve with this
code. Suppose I have function F-1:

(defun f-1 ()
  (handler-case
      (f-2)
    (t (condition)
      (warn "Error in F-2: ~S" condition))))

which will call F-2 and print warning if there was something wrong and
would continue running. Now F-2 looks something like this:

(defun f-2 ()
  (do-thing-1)
  (loop with foo = t
      while foo
      do (restart-case
             (error "I don't like value of FOO: ~S" foo)
           (change-foo (new-foo)
               :report "Set FOO"
               :interactive (lambda ()
                              (format t "Enter new value: ")
                              (multiple-value-list (eval (read))))
             (setf foo new-foo))))
  (do-thing-2))

which I would like to DO-THING-1, then the thing with FOO and
eventually DO-THING-2. And if there is an error while processing FOO,
I would like to enter the debug mode with my restart available (as it
would happen if there would be no HANDLER-CASE active). If there is an
error in DO-THING-1 or DO-THING-2 then the HANDLER-CASE in F-1 would
be activated, WARN would be called and processing would continue
(skipping part about special variables changing behavior of WARN).

One way to achieve this would be to use HANDLER-BIND, as you
suggested. Are there any other ways to do what I want (I know there is
so I'm reading section 9 of CLHS; should have done before posting)?

Janis Dzerins
-- 
  If million people say a stupid thing it's still a stupid thing.
From: Barry Margolin
Subject: Re: Q: handler-case and restart-case
Date: 
Message-ID: <kDq%5.19$vM5.425@burlma1-snr2>
In article <··············@asaka.latnet.lv>,
Janis Dzerins  <·····@latnet.lv> wrote:
>Thanks for insight and let me state what I wanted to achieve with this
>code. Suppose I have function F-1:
>
>(defun f-1 ()
>  (handler-case
>      (f-2)
>    (t (condition)
>      (warn "Error in F-2: ~S" condition))))
>
>which will call F-2 and print warning if there was something wrong and
>would continue running. Now F-2 looks something like this:
>
>(defun f-2 ()
>  (do-thing-1)
>  (loop with foo = t
>      while foo
>      do (restart-case
>             (error "I don't like value of FOO: ~S" foo)
>           (change-foo (new-foo)
>               :report "Set FOO"
>               :interactive (lambda ()
>                              (format t "Enter new value: ")
>                              (multiple-value-list (eval (read))))
>             (setf foo new-foo))))
>  (do-thing-2))
>
>which I would like to DO-THING-1, then the thing with FOO and
>eventually DO-THING-2. And if there is an error while processing FOO,
>I would like to enter the debug mode with my restart available (as it
>would happen if there would be no HANDLER-CASE active). If there is an
>error in DO-THING-1 or DO-THING-2 then the HANDLER-CASE in F-1 would
>be activated, WARN would be called and processing would continue
>(skipping part about special variables changing behavior of WARN).
>
>One way to achieve this would be to use HANDLER-BIND, as you
>suggested. Are there any other ways to do what I want (I know there is
>so I'm reading section 9 of CLHS; should have done before posting)?

If I understand you correctly, you will have to use HANDLER-BIND.

There are two general models of exception handling in programming
languages: resumable and rewinding (I'm not sure these are the actual terms
that computer scientists use).  Most languages only implement one or the
other.  However, Common Lisp provides both: HANDLER-BIND implements
resumable handlers, while HANDLER-CASE implements rewinding handlers.  If
you use HANDLER-CASE, the stack is unwound, and returning from the handler
returns from HANDLER-CASE itself, it doesn't go back to the place where the
error occurred, nor does it continue looking for other condition handlers.

-- 
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.