Re: Readability of exception handling

Jan Wielemaker <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
On 04/02/2014 12:40 PM, Parker Jones wrote:
>
> A lot of helpful information so far.  And a tutorial on exception
> handling would definitely be a great help.
>
> Still no examples of good exception handling though.  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?

Example of lazy programming. When using thread_signal/2, you should
always be aware that the thread might just have died, even if you just
tested it is still alive. Clean code should use this because the goal
may also raise an exception because throw(abort) doesn't fit on the
stack or because there is a timeout (which is implemented as an
exception).

	catch(thread_signal(Id, throw(abort)),
	      error(existence_error(thread, Id), _),
	      true).

It is indeed just ignoring the error.  Other common one is

	catch(Goal, Error,
	      print_message(warning, Error))

if you want to ignore the error, but give the user a warning.
Often the recovery is like this to make it a logical failure.

	catch(Goal, Error,
	      (print_message(warning, Error),fail))

> 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))
>          ).
>
> The pattern
>      (catch(_, Exception, true) -> (var(Exception) -> .. ; ..) ; .. )
> occurs frequently in the libraries. Could someone explain how this
> works?  I understand there is no handler (just true/0) and then a check
> to see if Exception is instantiated.

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.

> library/statistics.pl:160
> time(Goal) :-
>          time_state(State0),
>          (   call_cleanup(catch(Goal, E, (report(State0,10), throw(E))),
>                           Det = true),
>              time_true(State0),
>              (   Det == true
>              ->  !
>              ;   true
>              )
>          ;   report(State0, 11),
>              fail
>          ).
>
> An example with call_cleanup.  I find this really hard to read.  Is it
> just me?

Its a trick I learned from Ulrich Neumerkel. If you want to know that G
succeeded deterministically, do

	call_cleanup(G, Det = true)

If G leaves a choice point, the cleanup is not called.  You need
Det == true because Det is either unbound or 'true'.

> I find this syntax more accessible:
> try { }
> catch(e1) { }
> catch(e2) { }
> finally {  }
>
> I don't suppose there is a way of writing exception handlers like this
> and translating to ISO compliant predicates?

It is this:

	catch(Goal, E, true),
	(   nonvar(E)
	->  (   E = e1 --> ...
	    ;   E = e2 --> ...
	    ;   ...			% finally
	    )
	;   true
	).

Possibly, you'd like term_subsumes/2 instead of E = <term>

You can define this in SWI7 by defining {} as an infix and
postfix operator.  Then define a suitable goal_expansion/2 rule
to generate the code above and you're done.

The reason why I use the catch(..., E, true) often is that it
allows to handle different exceptions differently.  The other
reason is a (small) efficiency gain because no complex error
term needs to be pushed, neither a complex recovery goal.

	Cheers --- Jan

P.s.	Sometimes wished there was a symbolic version for
	term_subsumes/2 that contains >.  In quite a few
	cases it is a meaningful selector, but it is very
	verbose and I can never remember which side is the
	general and which the specific term :-(
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.