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