Re: Readability of exception handling
"Richard A. O'Keefe" <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <EAFD71EF-2FA7-4CE9-8CD7-94DE1EA46C61__3496.7178050012$1396527855$gmane$org@cs.otago.ac.nz> |
On 2/04/2014, at 11:40 PM, Parker Jones wrote:
> Still no examples of good exception handling though.
Exception handling in any programming language is paradoxical.
You need to wrap an exception handler about code <<C>>
because you do not trust <<C>> to work properly.
But on the other hand, you *DO* trust <<C>> to do so
very little damage to its environment that it is safe
to continue.
When we introduced exception handling into the Quintus
library, we wanted to make sure that every built in
predicate raised a sensible error if something went
wrong. Sometimes it was easy to check this before
starting to do anything. But sometimes we used
outer(...) :-
catch(inner, hack(Info), throw(...outer...)).
where somewhere deep inside inner, a problem was detected,
and throw(hack(...)) done, providing enough information
for outer's handler to repackage it and rethrow it as an
exception about the arguments of outer (as opposed to an
exception about secret innards).
Other cases of exception handling that I would care to
defend were things like arranging for a query to an
external data base to be aborted and the connection closed,
closing sockets, removing scratch files, &c.
Off hand, I think the only time we resumed execution after
an exception was at top level (including top level of
loading a file).
So "good exception handling" does one of two things:
- clean up and rethrow
- translate an exception that references internal
details to one that references the public interface
and rethrow.
General fix-and-resume exception handling requires you
to know exactly what changes the broken code has made
so that you can undo them. The examples I've seen
where this was done _well_ in any programming language
(not least Java) can be counted on the fingers of one ear.
> So here are a few snippets from the libraries which I presume are examples of good practice.
>
> library/thread.pl:250
> catch(thread_signal(Id, throw(abort)), _, true)
>
> If there are any exceptions just catch them and do nothing. Right?
Is that good practice or simple despair?
>
>
> library/thread.pl:379
> solve(Goal, Var, Queue) :-
> thread_self(Me),
> ( catch(Goal, E, true)
> -> ( var(E)
> -> thread_send_message(Queue, done(Me, Var))
> ; thread_send_message(Queue, error(Me, E))
> )
> ; thread_send_message(Queue, failed(Me))
> ).
This is basically an adapter, turning
success/failure/exception into messages.
Prolog being Prolog, it's a pity that we don't
get a stream of messages
<<done(Me,Var)* (failed(Me)|error(Me,Err))>>
> An example with call_cleanup. I find this really hard to read.
You are not meant to read it.
> Is it just me?
>
> I find this syntax more accessible:
> try { }
> catch(e1) { }
> catch(e2) { }
> finally { }
Yes, but it doesn't do the same thing.
catch(Goal, Pattern, Handler)
can succeed many times if Goal does.
This makes nonsense of 'finally'.
>
> I don't suppose there is a way of writing exception handlers like this and translating to ISO compliant predicates?
If you can give it a *semantics*, yes of course.