Re: Readability of exception handling
Jan Wielemaker <[email protected]> Mon, 7 Apr 2014 17:18:31 +0200
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
On 04/07/2014 04:49 PM, Parker Jones wrote:
> > Exceptions can be any term, but not variables. I.e., throw(_) itself
> > is an error. catch(Goal, E, true) thus succeeds if Goal succeeds or
> > throws an error. Iff goal succeeded, E is a variable.
>
> It does the trick, but it's a bit of a roundabout way of expressing things.
<snip>
> Perhaps it's due to my inexperience that I have to decrypt my own code
> to understand it. Either that or it's simply not possible to write
> readable code beyond the simplest cases using ISO syntax exception
> handling. I'd be a lot happier with a bit of syntactic sugar.
I showed one example of sugar. You can invent your own less C-like
versions, such as
on_exception(Goal,
[ Term => { Goal },
...
]).
etc.
> I'd be very interested to see something like a robust RPC server written
> in readable Prolog. There's a lot that can go wrong with an RPC call
> and it all has to be handled safely. Meanwhile I dread discovering a
> bug in my exception code in 12 months time and having to come back to
> it.
Hmmm. Most of the issue is avoiding leaking (stream-)handles. Use
(nested) setup_call_cleanup/3 for that. Jeff Rosewald (in library(tipc))
uses this:
==
eventually_implies(P, Q) :-
setup_call_cleanup(P, (Foo = true; Foo = false), assertion(Q)),
Foo == true.
:- op(950, xfy, ~>).
~>(P, Q) :- eventually_implies(P, Q).
==
so, you can write code like this
open(Something, read, In) ~> close(In),
do whatever you like, !.
It looks nice. In particular, it is way nicer if you need multiple
handles. For example:
==
copy_file(From, To) :-
open(From, read, In) ~> close(In),
open(From, write, Out) ~> close(Out),
copy_stream_data(In, Out), !.
==
vs.
==
copy_file(From, To) :-
setup_call_cleanup(
open(From, read, In),
setup_call_cleanup(
open(From, write, Out),
copy_stream_data(In, Out),
close(Out)),
close(In)).
==
What I don't like is that ~> overloads the ! to both prune choice
points and reclaim resources.
Typically, I see myself using three patterns dealing with exceptions:
- setup_call_cleanup/3 to reclaim resources.
- catch(Goal, Error, true) typically to catch errors and send them
over some communication channal.
- catch(Goal, Error, print_message(warning, Error)) to ignore (but
warn) about some error and continue.
I rarely test on specific errors. Occasionally on existence errors,
as in,
catch(thread_signal(victim, abort),
error(existence_error(thread,_),_),
true).
I'm curious in real code where error handling must be more subtle.
Cheets --- Jan