Subject: RE: Predicate Determining the port in which was called.

From: Jan Wielemaker <[email-masked]>
Date: Fri, 11 Aug 2000 09:38:58 +0200

On Fri, 11 Aug 2000, Douglas Miles (Volt Computer) wrote:

>*WOW* I love prolog  :)
>
>Thats very iteresting about the 
>
>"call,(exit,redo)*,(deterministic_exit|exit,cut|fail)"
>
>So there are 3 unique finishing states?
>
>* deterministic_exit
>* exit,cut
>* fail

Actually 4: there are also exceptions.  In addition, deterministic
exit is a bit vague as sometimes non-deterministic exit could actually
have been deterministic, but Prolog may fail to see it (either because
it doesn't look at exclusive cases or because hashing yields the same
key for two values of the first argument that are actually
different).

Altogether, call and fail are easy, and so are exceptions.

With some extremely dirty hacking using prolog_current_frame/1 and
prolog_frame_attribute/3 you can at runtime find out about Prolog's
notion of the last choice-point.  This is highly non-portable.

You can find out about being cut in a foreign predicate, but currently
not in a pure Prolog predicate.  Unfortunately, a call to a foreign
predicate can only initiate and complete a query back to Prolog.  I.e.
it is not possible to write call/1 as a foreign predicate keeping the
non-deterministic behaviour of the argument and monitoring the ports.
If you want to give up non-determinism that is a simple way to realise
a good clean interface.

I've seen various proposals around to deal with these issues.  I'm
in favour of something along these lines

	guard(Goal, Event, Guard)

Where, if Event is unified to the event (call, redo, fail, exit,
deterministic_exit, cut, exception(Exception)) Guard will be
called.

Should deterministic_exit be the sequence exit, cut?

How should bindings and exit status of the guards be handled? 
Best might be to undo bindings, ignore success/failure and
force them to be deterministic.  Only exceptions of the guards
should have effect (so a DB commit from the cut guard raising
an exception will force the whole to exit with an exception). 	

This seems to fit nicely with catch/3.  Not really thought of
it in detail, but I my first guess is it should be feasible to implement
this without too much trouble and without performance degradation.

Another issue is that you may wish to handle bindings of the call
guard in the others (like the transaction id).  You could still
trick that using

	make_transaction(Transaction),
	guard(Goal, Event, my_guard(Event, Transaction)),
	...

my_guard(call, _).
my_guard(fail, Transaction) :-
	<rollback>.
...

Not really clean, but acceptable, I would say.

	Regards --- Jan