Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Accumulated and never suppressed exceptions (setup_call_cleanup/3)

22 views
Skip to first unread message

Jan Burse

unread,
Sep 23, 2011, 1:43:39 PM9/23/11
to
Dear All

May I show you the exception handling of 2012 for
setup_call_cleanup/3, a little experiment thereof.
Idea is never to improve exception handling for
the cleanup part of the following built-in
predicate:

setup_call_cleanup(S, G, C):
S: setup part
G: goal
C: cleanup part

As the title says, idea is to do the following
for the cleanup part:
1) Never supress exceptions
2) Accumulate exceptions

Here are some result:

?- [user].
nondet :- true.
nondet :- fail.

Yes
?- setup_call_cleanup(true,nondet,throw(x)), throw(y).
Unknown exception: x
Unknown exception: y
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,nondet,throw(y)), throw(z).
Unknown exception: x
Unknown exception: y
Unknown exception: z
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,nondet,throw(y)), !.
Unknown exception: x
Unknown exception: y
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,nondet,throw(y)), fail.
Unknown exception: x
Unknown exception: y
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,nondet,throw(y)).
Yes
Unknown exception: x
Unknown exception: y
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,!,throw(y)).
Unknown exception: x
Unknown exception: y
?- setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,fail,throw(y)).
Unknown exception: x
Unknown exception: y
?-

How does it work? Well very simple. Start with
defining a new throw ball and its handling in
the top level. The throw ball I have defined is:

cause(first,second)

Then change the contract of the cutter. The cutter
is the object that handles removing choice points.
It is not only involved in setup_call_cleanup/3
but also in regular removal of choice points.
So change it from:

void cut()

To:

void cut() throws Exception;

Now you can extend your cutters that they don't
suppress exceptions. But we are not yet finished.
We need also to accumulate exceptions. Therefore
whenever you do remove a couple of choice points
forcefully not by backtracking, for example as
a result of a cut or an exception event, you can do
the following:

foreach (cp) {
try {
cp.cut();
} (Exception x) {
if (e==null) {
e=x;
} else {
e=makeCause(x,e);
}
}
}

Thus you will not only cut away the choices,
but also accumulate eventual exceptions. The
accumulation is done in the variable e. The
variable e is initialized according to the
event:

Cut:
e = null;
Exception y:
e = y;

After the loop you can return e to the toplevel
or if there is a catch/3 around, you can hand
it to this. So the following also works:

?- catch((setup_call_cleanup(true,true,throw(x)), throw(y)),E,true).
E = cause(x, y)
?- catch((setup_call_cleanup(true,true,throw(x)),
setup_call_cleanup(true,true,throw(y)), !),E,true).
E = cause(x, y)

Drawback of the approach? Yes there is a kind of
a drawback when I compare to other programming languages
that implement exception chaining.

In Java for example an exception can have an optional
cause, but this cause does not influence the catch.
So the following code works independent of whether
the x.getCause() is null or not:

try {
...
} catch (myException x) {
...
}

In Prolog we would need to convert a code:

catch(.., my_exception(E), ..)

Into the following code;

catch(catch(..,
my_exception(E), ...),
cause(my_exception(E),_), ...))

I did not find a way around for this problem. So if
you aspire for a particular exception in the cleanup,
then your code gets a little bit more complicated.

Eventually there would be a way around for throw
balls of the form error(_,_). We could stash the
cause in the implementation dependent part of this
term. But then the above examples with arbitrary
throw balls does not work.

Comments welcome.

Best Regards



Jan Burse

unread,
Sep 23, 2011, 1:47:10 PM9/23/11
to
Jan Burse schrieb:
> Idea is never to improve exception handling for
Oops, should read:

Idea is to improve exception handling for

Jan Burse schrieb:
>
> ?- catch((setup_call_cleanup(true,true,throw(x)), throw(y)),E,true).
> E = cause(x, y)
> ?- catch((setup_call_cleanup(true,true,throw(x)),
> setup_call_cleanup(true,true,throw(y)), !),E,true).
> E = cause(x, y)

Oops, should read:

?- catch((setup_call_cleanup(true,nondet,throw(x)), throw(y)),E,true).
E = cause(x, y)
?- catch((setup_call_cleanup(true,nondet,throw(x)),
setup_call_cleanup(true,nondet,throw(y)), !),E,true).
E = cause(x, y)

Ulrich Neumerkel

unread,
Sep 26, 2011, 8:26:23 PM9/26/11
to
Jan Burse <janb...@fastmail.fm> writes:
...

>Drawback of the approach? Yes there is a kind of
>a drawback when I compare to other programming languages
>that implement exception chaining.
>
>In Java for example an exception can have an optional
>cause, but this cause does not influence the catch.

Java does not provide exception chaining on the language level.
try finally discards the exception of the try block should
there be an exception in the finally block.

This convention was taken from Common Lisp. But Common Lisp's
mechanisms are much more evolved. See the discussion 2009-01
comp.lang.lisp "Rationale behind unwind-protect and double errors"

If you want to simulate the convention of exception chaining in Java
you can use the second argument of error/2 for that purpose. Which
also solves

> catch(catch(..,
> my_exception(E), ...),
> cause(my_exception(E),_), ...))

>I did not find a way around for this problem. ...

Jan Burse

unread,
Sep 27, 2011, 2:55:14 AM9/27/11
to
Ulrich Neumerkel schrieb:

> Java does not provide exception chaining on the language level.
> try finally discards the exception of the try block should
> there be an exception in the finally block.

There are actually two programming styles. One
style is using finally, and the other is
using catch/throw.

If you use catch/throw you have the option to do
exception chaining since Java 1.4 since the
exception base class Throwable offers the
cause field and corresponding enhanced constructors.

So basically before Java 1.4 a simple
exception could be only created as follows:

Throwable()
Throwable(String)

Subclasses can replace String by a subclass
specific Info. After Java 1.4 there is also:

Throwable(Throwable)
Throwable(String, Throwable)

The printStackTrace() method after Java 1.4
supports printing these chained exceptions
in a clever way by finding common frames
in the main exception and in the causing
exception.

** Question Begin **

Any Prolog arround that can do the same?
In my current implementation of cause/2
I just print twice the stack trace of
the first and second exception. Ok, it
might be not so difficult to do...

** Question End **

Since Throwable is part of java.lang it can
be said that since Java 1.4 exception chaining
is supported on the language level.

This exception chaining is heavily used nowadays
in Java and is very useful. Some prominent
chained exceptions are:

InvocationTargetException
ServletException (cause is called root cause there)
RemoteException (cause is called detail)
SQLWarning (cause not allowed for SQLException)

The programming pattern to create a chain
exception is very simple. It is not lightweight
if really an exception happens, since a new
object needs to be created:

try {
/* do something */
} catch (myExceptionInternal x) {
throw new myExceptionExternal("my exception external", x);
}

Although the programming pattern is creates a
new object in case of a chaining, the pattern
is very lightweight when no exception occurs,
since in Java try/catch does not incure any
overhead to running code when no exception occurs.
Only when an exception occurs the stack trace
and the try/catch region maps of the methods are
inspected.

*** Question Begin **

Any Prolog around that also has try/catch
region maps and thus lightweight exceptions? The
ISO standard suggests creating a choice point.
Would it be possible at all for Prolog to use
region maps in the presence of cut and/or
tail recursion?

*** Question End **

Since release Java 1.7 there is even something new ,
expressions in the chain can be marked as
suppressed. I guess it could be used to make a
non spurious finally by a try/catch. So that you
have some feature of the finally, that exceptions get
suppressed, and some feature of chained exceptions,
that they will be nevertheless shown in the top-level.

From the doc:

* In these situations, only one of the thrown exceptions can be
* propagated. In the {@code try}-with-resources statement, when
* there are two such exceptions, the exception originating from
* the {@code try} block is propagated and the exception from the
* {@code finally} block is added to the list of exceptions
* suppressed by the exception from the {@code try} block. As an
* exception unwinds the stack, it can accumulate multiple
* suppressed exceptions.

It looks that these suppressed exceptions not
exactly sit in the chain, but form an extra
info an exception can have. In Prolog we could
use suppressed/2 for that. Maybe should change
cause/2 of the cutter to suppressed/2 in the
first time... Good Idea! gr8

> If you want to simulate the convention of exception
> chaining in Java you can use the second argument
> of error/2 for that purpose. Which also solves

Only for error/2 balls. But not for arbitrary
balls. But maybe this might be ok.

Bye


Jan Burse

unread,
Sep 27, 2011, 3:19:34 AM9/27/11
to
Jan Burse schrieb:
>
> From the doc:
>
> * suppressed by the exception from the {@code try} block. As an
> * exception unwinds the stack, it can accumulate multiple
> * suppressed exceptions.

Interestingly the new try-with-resource statement in JDK 1.7
reminds me very much of setup_call_cleanup/3. It needs
as an argument a object that implements the following
interface:

public interface AutoCloseable {

void close() throws Exception;

}

The adding of suppressed exception does not work for the
normal try/finally. Only for the try-with-resource.
By giving the AutoClosable object the compiler can
generate code that calls this object in different
situation, and depending on the situation does a
a addSuppressed().

This is object is very similar to the cleanup part
of the setup_call_clean/3 construct. The cleanup
part is also called in different situations, and
depending on the situation, in my revised proposal
now, the resulting exception of the cleanup could
be added as a suppressed exception to the main
exception.

So the revised cleanup sweep would look as follows:

foreach (cp) {
try {
cp.cut();
} (Exception x) {
if (e==null) {
e=x;
} else {
// e=makeCause(x,e); /* old proposal */
e.addSuppressed(x); /* new proposal */
}
}
}

Could be done for setup_call_cleanup/3, in 2012.
Yes we can!

Actually since JDK 1.7.0 is already released need
not to wait for 2011, actually beeing a little
late, could do it now, so that it works
seemingless between the application programming
interface of the Prolog system (calling Prolog
from Java back and forth) and as well inside
the Prolog system with corresponding throw balls.

**********

But no hurry, first comments welcome!

***********

Best Regards

P.S.: Some JDK 1.7 test code:

Manual Suppressed:
------------------

public class TestSuppressed {

public static void main(String[] args) throws Exception {
try {
throw new IllegalArgumentException("x");
} catch (IllegalArgumentException x) {
x.addSuppressed(new IllegalArgumentException("y"));
throw x;
}
}

}

gives:

Exception in thread "main" java.lang.IllegalArgumentException: x
at TestSuppressed.main(TestSuppressed.java:12)
Suppressed: java.lang.IllegalArgumentException: y
at TestSuppressed.main(TestSuppressed.java:14)
... 5 more

Automatic Suppressed:
---------------------

public class TestSuppressed implements AutoCloseable {

public static void main(String[] args) throws Exception {
try (TestSuppressed ts=new TestSuppressed()) {
throw new IllegalArgumentException("x");
}
}

public void close() throws Exception {
throw new IllegalArgumentException("y");
}

}

gives:

Exception in thread "main" java.lang.IllegalArgumentException: x
at TestSuppressed.main(TestSuppressed.java:9)
Suppressed: java.lang.IllegalArgumentException: y
at TestSuppressed.close(TestSuppressed.java:20)
at TestSuppressed.main(TestSuppressed.java:10)
... 5 more

Jan Burse

unread,
Sep 27, 2011, 3:24:28 AM9/27/11
to
Jan Burse schrieb:
> The adding of suppressed exception does not work for the
> normal try/finally. Only for the try-with-resource.

Correction:
does not work *automatically*

Ulrich Neumerkel

unread,
Sep 27, 2011, 6:49:28 AM9/27/11
to
Jan Burse <janb...@fastmail.fm> writes:
>Interestingly the new try-with-resource statement in JDK 1.7
>reminds me very much of setup_call_cleanup/3.

Thanks for the reference to SE 7!

http://download.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions

In contrast to the old try-finally, try-with-resource
suppresses the "Cleanup" exception.

That's exactly how N215, N211 differ from the original
implementation in SICStus which copied Java's try-finally
which got it from Common Lisp's unwind-protect.

Also, it seems that Java has now a new way to retrieve
the suppressed exception via Throwable.getSuppressed.

Jan Burse

unread,
Sep 27, 2011, 7:30:43 AM9/27/11
to
Ulrich Neumerkel schrieb:
> http://download.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions
>
> In contrast to the old try-finally, try-with-resource
> suppresses the "Cleanup" exception.

Only when happening in the finally clause. But
since you have the AutoClosable interface, you
don't use the finally clause anymore in
try-with-resource. Look see the following code
I have already posted, it works:

Automatic Suppressed:
---------------------

public class TestSuppressed implements AutoCloseable {

public static void main(String[] args) throws Exception {
try (TestSuppressed ts=new TestSuppressed()) {
throw new IllegalArgumentException("x");
}
}

public void close() throws Exception {
throw new IllegalArgumentException("y");
}

}

gives:

Exception in thread "main" java.lang.IllegalArgumentException: x
at TestSuppressed.main(TestSuppressed.java:9)
Suppressed: java.lang.IllegalArgumentException: y
at TestSuppressed.close(TestSuppressed.java:20)
at TestSuppressed.main(TestSuppressed.java:10)
... 5 more

The JDK 1.7 did not change anything in the semantics
of the finally clause. But it added the new try-with-resource
idiom, which when correctly used without finally, will
not loose exceptions. Only add to the supress list of
the main exception.

> That's exactly how N215, N211 differ from the original
> implementation in SICStus which copied Java's try-finally
> which got it from Common Lisp's unwind-protect.

Could be that this is the right view on the history
of exception handling. Given the fact that it is said
that Gabriel worked for Java. But evolution cannot be
stopped by history, or bureaucrats that point to history.

I guess the pressure was high enough in the Java environment
to introduce try-with-resource which exactly solves around
the finally problem. Please note you can put what ever
cleanup code into the class that implements the AutoClosable
interface.

So it would be really on equal foot with a setup_call_cleanup
that does not loose some exceptions, but instead accumulates
them in a suppressed list of the main exception.

2012, yes we can!

Best Regards

Jan Burse

unread,
Sep 27, 2011, 7:36:09 AM9/27/11
to
Jan Burse schrieb:
>
> So it would be really on equal foot with a setup_call_cleanup
> that does not loose some exceptions, but instead accumulates
> them in a suppressed list of the main exception.
>
> 2012, yes we can!

But I don't know yet what the views on try-with-resource are
when used without the finally. I have already googled a little
bit. Maybe the old approach was to put some logging into
the finally clause.

The logging can then be used when the exception pops up in
the top-level to do some bug hunnting. But imagine you have
multiple processes or a multi tier application.

Now with the try-with-resource when the exceptions pops up
in the top-level it also carries the suppressed exceptions
and you can easily inspect it from there. No need to look
for log files scattered from earth to mars.

Bye

Jan Burse

unread,
Sep 27, 2011, 7:38:41 AM9/27/11
to
Jan Burse schrieb:
> Now with the try-with-resource when the exceptions pops up
> in the top-level it also carries the suppressed exceptions
> and you can easily inspect it from there. No need to look
> for log files scattered from earth to mars.

Well your web server would also need to be able to
marshall the full exception. How is this done in
some Prolog system extensions that allow remote calls?
Will the throw ball be serialized and then de-serialized
again in case the service throws an exception?

Bye

Ulrich Neumerkel

unread,
Sep 27, 2011, 10:24:39 AM9/27/11
to
Jan Burse <janb...@fastmail.fm> writes:
>I guess the pressure was high enough in the Java environment
>to introduce try-with-resource which exactly solves around
>the finally problem. Please note you can put what ever
>cleanup code into the class that implements the AutoClosable
>interface.
>
>So it would be really on equal foot with a setup_call_cleanup
>that does not loose some exceptions, but instead accumulates
>them in a suppressed list of the main exception.

Not entirely. setup_call_cleanup/3 has also an argument Setup
which is protected against interrupts. What is the analogon
to this in Java?

Jan Burse

unread,
Sep 27, 2011, 11:05:10 AM9/27/11
to
Ulrich Neumerkel schrieb:
> Not entirely. setup_call_cleanup/3 has also an argument Setup
> which is protected against interrupts. What is the analogon
> to this in Java?

You need two threads. One controller and one target thread.
The controller can inject an exception to the target thread
at any time. By doing the following:

Thread target;

void control() {
target.stop();
}

If you want to listen to aquired regions by the target thread
you can use a lock. By doing the following:

Object lock = new Object();

void control() {
synchronized(lock) {
target.stop();
}
}

And the target thread can protect its region as follows:

synchronized(lock) {
...
protected region
...
}

If target.stop() is too drastic you can also do other
things so as too end in a thread_signal/1 semantics
where you can hand over a goal before a call-port.

Bye

Jan Burse

unread,
Sep 27, 2011, 11:07:36 AM9/27/11
to
Jan Burse schrieb:
> If target.stop() is too drastic you can also do other

You can also replace synchronized(..) by other means,
for example pick something from java.util.concurrent.

Bye

Jan Burse

unread,
Sep 27, 2011, 1:15:10 PM9/27/11
to
Ulrich Neumerkel schrieb:
> Not entirely. setup_call_cleanup/3 has also an argument Setup
> which is protected against interrupts. What is the analogon
> to this in Java?

But you probably don't want only to know how
Java could protect against interrupts but
how this is put to use in a try-with-resource
so as to get a deterministic (without backtracking)
variant of setup_call_cleanup/3 that has also
accumulation of suppressed exceptions.

Here is how the code reads, just replace
*enter region* and *exit region* by your
preferred method to protect from the interrupts
you are fearing during the setup:

public class TestSuppressed implements AutoCloseable {

public TestSuppressed() {
*enter region*

.. Your setup code ..

*exit region*
}

public static void main(String[] args) throws Exception {
try (TestSuppressed ts=new TestSuppressed()) {

.. Your call code ..
}
}

public void close() throws Exception {

.. Your clean-up code ..

}

So idea is basically to do the setup in the expression
that defines the auto closeable resource. This guarantees
what post-N215 also requires, either once(S) plus
cleanup installation as a whole succeeds or else once(S)
fails and no cleanup is installed.

In my example above it happens that the expression that
defines the auto closeable source is a constructor.
And I do the protection against interrupts in this
constructor. According to the spec of try-with-resource
if the expression that defines the auto closeable
resource throws an exception than this exception is
returned, this would probably also correspond to post-N215.

The spec of try-with-resource is found here:
http://blogs.oracle.com/darcy/entry/project_coin_updated_arm_spec

What we don't have in Java is that once(S) fails in
the sense of a failure that would induce backtracking.
I also don't know exactly what happens if a null
expression is provided. The spec shows the translation
of the try-with-resource into a try-catch-finally.
Namely it is that:

If V2 is an error (i.e. a Throwable that
is not an Exception), then the try-with-resources
statement completes abruptly because of
the throw of value V2. In this case, V1
is not suppressed by V2.

Since in the finally not all exceptions are suppressed,
we might eventually adopt the same for a Prolog system.

But Problem is a little bit that Prolog does not
distinguish between checked and unchecked exceptions.
But many Prolog systems do, they have some exceptions
for internal use that do not follow the error(_,_)
scheme or maybe follow the error scheme but are
for example not visible in a catch/3.

But leaving aside for the moment the details of the
distinction between checked and uncheck exceptions
we could do the following:

/* Version 0.3a Exception Accumulation Proposal */

foreach (cp) {
try {
cp.cut();
} catch (Exception x) {
if (e==null) {
e=x;
} else {
// e=makeCause(x,e); /* version 0.1 */
e.addSuppressed(x); /* version 0.2 */
}
} catch (Throwable x) { /* version 0.3a */
e=x;
}
}

Or as the Java spec also suggests when you scroll
down the blog page refered by the http link
from before:

/* Version 0.3b Exception Accumulation Proposal */

foreach (cp) {
try {
cp.cut();
} catch (Exception x) {
if (e==null) {
e=x;
} else {
// e=makeCause(x,e); /* version 0.1 */
e.addSuppressed(x); /* version 0.2 */
}
} catch (Throwable x) { /* version 0.3b */
if (e!=null) {
x.addSuppressed(e);
}
e=x;
}
}

Best Regards

Jan Burse

unread,
Sep 29, 2011, 12:11:32 PM9/29/11
to
Jan Burse schrieb:
> [...]
>
> As the title says, idea is to do the following
> for the cleanup part:
> 1) Never supress exceptions
> 2) Accumulate exceptions
>
> Here are some result:
>
> ?- [user].
> nondet :- true.
> nondet :- fail.
>
> Yes
> ?- setup_call_cleanup(true,nondet,throw(x)), throw(y).
> Unknown exception: x
> Unknown exception: y
>
> [...]


Little bit more work on accumulated exceptions revealed
an error in the above examples. The issue is the ordering
of the error display. Its probably better to show the
very first error first and then the suppressed errors.

A further issue is how the top-level should react on
these errors. And also how query answer loops created
by break/0 should react on these errors. Here is my
penny of thought:

The top level or break detects known system
errors. If we have a exception cause chain
rest then this is depicted besides it the
usual action the top level or break
would perform.

So in summary this gives the following examples:

[1] ?- [user].
nondet :- true.
nondet :- fail.
a :- setup_call_cleanup(true, nondet, throw(a)).
b :- setup_call_cleanup(true, nondet, throw(b)).

Yes
[1] ?- break.
[2] ?- setup_call_cleanup(true, (a,b), throw(x)), throw(y).
Unknown exception: y
Unknown exception: b
Unknown exception: a
Unknown exception: x

So the order of display of the exceptions above is now
right, in order of when the exceptions occured. The
initial exception first. And then the suppressed
exceptions in the reverse of the order the corresponding
clean-ups have been installed.

[2] ?- setup_call_cleanup(true, (a,b), throw(x)), abort.
Unknown exception: b
Unknown exception: a
Unknown exception: x

The "abort" throw ball does not kick the end-user out of
the current top-level or break session. We stay in the
current session. So we simply do print the exception
cause chain rest, i.e. all suppressed exceptions. Some Prolog systems
might also print first "Execution Aborted" or some such.
We take the sudden re-appearance of the prompt as an indicative
for that.

[2] ?- setup_call_cleanup(true, (a,b), throw(x)), exit.
Unknown exception: b
Unknown exception: a
Unknown exception: x
Yes
[1] ?-

The "exit" throw ball does kick the end-user out of
the current top-level or break session. We don't stay
in the current session. It does the same like ^D, but
can be called during execution. Not all Prolog systems
have exit/0.

Again we simply do print the exception cause chain rest,
i.e. all suppressed exceptions. Some Prolog systems
might also print first "Exit break level" or some
such. We take the sudden re-appearance of the prompt
with a lower break level as an indicative for that.

The very first example could go into a new ISO proposal.
But top-level and break sessions are probably out of
the scope of ISO. There is one corner in the current
ISO standard where it is defined that a throw/1 that
doesn't find a catch/3 handler gives a system error.

In a language binding view we could also say the
current throw ball is converted to a representation
that the calling system understands and then handed
to the calling system. When the calling system has
a interface definition language that allows expressing
thrown exception this could be done as follows:

boolean unfoldFirst(Goal) throws PrologException;

boolean unfoldNext() throws PrologException;

void unfoldClose();

void unfoldCut();

This is the interface definition prior to the introduction
of a cutter. Not shown are futher handles passed around.
With the introduction of a cutter, the following seems
more appropriate:

void unfoldClose() throws PrologException;

void unfoldCut(PrologException e) throws PrologException;

So the unfoldClose() method that terminates the search
by the Prolog processor now potentially also throws an
exception. This can happen when during termination an
installed cleanup handler is called and this handler
throws an exception. When multiple handler exceptions
occur they are accumulated.

Further the unfoldCut() method that removes all
choice points from the current search of the Prolog
processor now also potentially throws an exception,
for similar reasons as the unfoldClose(). But the
unfoldClose() will undo bindings, whereas the unfoldCut()
will not, and the search results can further be used.
The unfoldCut() might also take an already existing
exception for accumulation.

Best Regards





Mostowski Collapse

unread,
Feb 21, 2023, 3:15:20 PM2/21/23
to
Woa! 12 years later some new ideas! Holy Cow. Following
the ideas of Python I have now a prototype where catch/3
only matches the head of the chained exception:

/* SWI-Prolog 9.1.4 */
?- call_cleanup(throw(foo), throw(bar)).
ERROR: Unhandled exception: Unknown message: foo
ERROR: Unhandled exception: Unknown message: bar
?- catch(call_cleanup(throw(foo), throw(bar)), E, true).
E = foo.

/* Jekejeke Prolog 1.5.6 */
?- call_cleanup(throw(foo), throw(bar)).
Unknown exception: foo
Unknown exception: bar
?- catch(call_cleanup(throw(foo), throw(bar)), E, true).
E = foo.

In case somebody wants to access the full chained
exception, one can still use sys_trap/3:

/* Jekejeke Prolog 1.5.6 */
?- sys_trap(call_cleanup(throw(foo), throw(bar)), E, true).
E = cause(foo, bar).

Maybe this is less comfortable than Python, since the
suggestion is two different catchers, catch/3 and sys_trap/3.
Whereas Python has always the same catcher, but different throwers.

Mostowski Collapse

unread,
Feb 21, 2023, 3:20:12 PM2/21/23
to
As a bonus, all the setup_call_cleanup/3 test cases as per
WG17 N215 should now pass, even in the presence of exception
chaining, when they are written with the help of the new catch/3.

The test case report shows me now:

Ok Nok Predicate
26 0 call_cleanup/2
38 0 setup_call_cleanup/3

BTW: What is the Python Exception Chaining? Check this out:
https://docs.python.org/3/tutorial/errors.html#exception-chaining

The thingy also works with the Logtalk idiom in Dogelog
Player. There I now have as well the following behaviour,
for the new once_cleanup/2:

/* Dogelog Player 1.0.4 */
?- once_cleanup(throw(foo), throw(bar)).
foo /* in red color */
bar /* in red color */
?- catch(once_cleanup(throw(foo), throw(bar)), E, true).
E = foo.
?- sys_trap(once_cleanup(throw(foo), throw(bar)), E, true).
E = cause(foo, bar).

Mostowski Collapse

unread,
Feb 21, 2023, 3:33:38 PM2/21/23
to

Ha Ha, Dogelog Player has thrown the cutter overboard,
but keeps the exception chaining, at least for the Logtalk idiom.

Mostowski Collapse

unread,
Feb 21, 2023, 7:49:15 PM2/21/23
to
I blame it on the stars, 2011 was A Year of the Cat and
so is 2023. It is believed to bring good luck and smooth sailing.

al stewart 1979 long live version
https://www.youtube.com/watch?v=AazEMt0eoFg

Lets make 2023 a splendid year for all Prolog systems!
0 new messages