However, I was wondering if anyone have written a library version, as
there are some fiddly bits to the process.
Here is a simple version I use for a common case:
(defmacro default-handler ((&optional (type 'error)
condition
signaling-form)
(&rest expression)
&body handler)
(let ((block-name (gensym))
(handle (gensym))
(c (gensym))
(condition (if condition condition (gensym))))
`(let (,condition)
(block ,block-name
(tagbody
(handler-bind
((,type (lambda (,c)
(signal ,(if signaling-form signaling-form c))
;;otherwise handle
(setf ,condition ,c)
(go ,handle))))
(return-from ,block-name ,expression))
,handle
(return-from ,block-name (progn ,@handler)))))))
Does anyone else use default handlers as an idiom?
Matt
I just spent about 30 seconds looking at this, so take these comments
for what they're worth at a quick glance in case they're helpful.
Overall, this macro isn't a syntax that I would personally have picked
(not that that should matter to anyone), but maybe in part because you
don't offer documentation in comments, especially to include sample
uses, which might help understand what the various code parts are for
in practice. It's not obvious to me that the resignaling form is a
good idea, but maybe there are uses of it. Probably it's a way for
users to confuse themselves and for common use something that doesn't
offer this is better.
The signaling-form seems least misnamed and maybe in the wrong place.
It is yielding a condition, not causing a signal. I suspect you want
,(if resignaling-form resignaling-form `(signal ,c))
or else you want
(signal ,(if condition-form condition-form c))
In either case, a renaming is warranted.
What is the reason for the (go ,handle) other than to make state that
would be useful to have unavailable? Instead of (go ,handle) I'd
rather see (return-from ,block-name (progn ,@handler)) right there and
then no need for the GO.
The variable named EXPRESSION is odd because it's an &rest but then ,
is used to insert it. Did you test this? (I didn't. :) I'd probably
use &body in that case, too, by the way. When it's code, you should
use &body, even if it's at a recursive layer. If it is a list of
things, you probably want ,@ to insert it.
Good luck.
--Kent
What would you pick for syntax than handles a single case?
> (not that that should matter to anyone), but maybe in part because you
> don't offer documentation in comments, especially to include sample
> uses, which might help understand what the various code parts are for
> in practice.
Well, despite my offering, it wasn't really meant to see the light of
day.
It was more of a "So, has anyone written a real one of these? Here's
my
toy version." kind of post.
> It's not obvious to me that the resignaling form is a
> good idea, but maybe there are uses of it. Probably it's a way for
> users to confuse themselves and for common use something that doesn't
> offer this is better.
>
> The signaling-form seems least misnamed and maybe in the wrong place.
> It is yielding a condition, not causing a signal. I suspect you want
> ,(if resignaling-form resignaling-form `(signal ,c))
> or else you want
> (signal ,(if condition-form condition-form c))
> In either case, a renaming is warranted.
>
Yeah, the name's not great. Also, in the version I use now
(setf ,condition ,c)
occurs before re-signaling, so the orginal condition is effectively in
scope.
As Common Lisp doesn't require that many different error types,
resignaling
is a quick and dirty way to emit a more specific error.
> What is the reason for the (go ,handle) other than to make state that
> would be useful to have unavailable? Instead of (go ,handle) I'd
> rather see (return-from ,block-name (progn ,@handler)) right there and
> then no need for the GO.
>
Well, I didn't think I was actually handling the condition until I did
a
control transfer. If I use return-from, doesn't the control transfer
happen
_after_ the handler "runs"? I don't have it in front of me, but I
think
modeled it on an equivalent macroexpansion of handler-case given in
CLTL2 or the Hyperspec.
> The variable named EXPRESSION is odd because it's an &rest but then ,
> is used to insert it. Did you test this? (I didn't. :) I'd probably
> use &body in that case, too, by the way. When it's code, you should
> use &body, even if it's at a recursive layer. If it is a list of
> things, you probably want ,@ to insert it.
Expression is a &rest variable simply to get emacs to indent the form
the
way I want w/o mucking about with the mode.
>
> Good luck.
> --Kent
Thanks, I could use it.
Matt
>> Overall, this macro isn't a syntax that I would personally have picked
>
> What would you pick for syntax than handles a single case?
In partial answer to my own question, I suppose it is idiomatic to use a
"with-" prefix to introduce what is basically a dynamic scope.
Matt
* "Matthew D. Swank" <Np8wo.8332$O64....@newsfe20.iad> :
Wrote on Fri, 22 Oct 2010 04:28:29 GMT:
| Does anyone else use default handlers as an idiom?
I use the more traditional "default restarts" as an idiom. Only this
gives the flexibility that I cannot do without. Typically I stash your
"common case" handler code in a "Continue" restart, so I can react
during incremental interactive development, and after enough testing to
make sure your "default handler" code is ok for the dataset at hand, I
typically wrap the top level call in a CONTINUING-FROM-ERRORS macro,
which currently has this much hair:
CONTINUING-FROM-ERRORS ((&rest condition-types) &body body) MACRO
"CONDITION-TYPES is a list of CONDITION-MATCHERs which specify the
conditions which we handle by invoking the CONTINUE restart automatically. If
CONDITION-TYPES is NIL, include ERROR. To disable handling any conditions,
use CONDITION-TYPE (:NONE). condition-types = (condition-matcher*);
condition-matcher = condition-type | (condition-type fmt*); A CONDITION-TYPE
is a symbol denoting a condition. FMT is a string which is tested against the
SIMPLE-CONDITION-FORMAT-CONTROL of the condition being handled: we continue
from the error only if there is a prefix match. If multiple FMT strings are
supplied, we continue from the error if any one of them match."
--
Madhu
> > Does anyone else use default handlers as an idiom?
>
> I use the more traditional "default restarts" as an idiom. Only this
> gives the flexibility that I cannot do without. Typically I stash your
> "common case" handler code in a "Continue" restart
...
> CONTINUING-FROM-ERRORS ((&rest condition-types) &body body) MACRO
>
> "CONDITION-TYPES is a list of CONDITION-MATCHERs which specify the
> conditions which we handle by invoking the CONTINUE restart
> automatically.
That interesting. I've been inching towards explicitly making restarts
part of a protocol. To that end I'll have something like:
(restart-case
(default-handler (deal-with-this-error e)
(something-bad-may-happen)
(default-restart e))
(restart-1 () ...)
(restart-2 () ...)
...
(default-restart ()
(dealing-with-the-error)))
Where "(default-restart e)" applies top level function that invokes the
same restart it names.
So, I suppose I also make the connection that a default handlers deserve
names. Ironically, by using restart-case, it doesn't matter that my
handler syntax performs a non-local exit to run the handler.
Matt