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

Exceptuations

0 views
Skip to first unread message

Yuval Kogman

unread,
Sep 25, 2005, 10:28:18 AM9/25/05
to perl6-l...@perl.org
Hi,

Suppose I'm writing a file browser, with a pane on the left to
display the filesystem hierarchy, and a frame on the right to
preview the file.

Suppose I have a convenience function, preview_file which takes a
path and returns a value that the frame display view knows to
render.

Let's define this for HTML files, where the desired preview is a
summary of the text:

use Text::Summarize;
use HTML::ToAscii;

multi sub preview_file ($filename where /\.html$/ ) {
my $handle = open($filename, :r); # might fail if $filename is unreadable
return summarize(html2ascii(=$handle)); # might fail if HTML is invalid
}

And this code is called when the user clicks on a file in the pane:

class NiftyUI {
use fatal;

method handl_some_click ($file) {
$.right_frame.display(preview_file($file.name));
}

method handle_event ($event) {
$?SELF.dispatch_event($event);

CATCH {
when NiftyBackend::Exception {
$?SELF.display_error_box($!);
}

default { die $! };
}
}
}

With the current shape of the code if any of the possible failures
in the backend code happen, they are reported in a message dialog.

Now, let's say we would like to add a feature, that lets the user
change the mode of the file if it's unreadable.

Several approaches to doing this:

* give the backend an abstract object, a Frontend of sorts:

$frontend.ask_user("do you want to make the file readable?")

* throw internal exceptions, and let the frontend handle the
exception and retry the action:

method handle_some_click ($file) {
$.right_frame.display(preview_file($file.name));

CATCH {
when IO::Errors::PERMISSION_DENIED {
if ($?SELF.ask_user_to_chmod_file($file)) {
make_readable($file);
$?SELF.handle_some_click($file); # retry the event
} else { die $! }
}
}
}

I propose a new model - each exception has a continuation that
allows it to be unfatalized.

The exception object has info on whether the fatality of the
exception was due to a die, or a use fatal, and lets the exception
handler decide accordingly.

Then we have code that looks like this:

method handle_some_click ($file) {
$.right_frame.display(preview_file($file.name));

CATCH {
when IO::Errors::PERMISSION_DENIED {
if ($?SELF.ask_user_to_chmod_file($file)) {
make_readable($file);
$!.continue(open($file, :r)); # the return value of the failed open is
# overridden by the return value of this (hopefully successful) open.
} else { die $! }
}
when HTML::ToAscii::Exception { # badly formed
$!.continue("The HTML file contained a syntax error");
# this string is passed to summarize instead of the undef exception object
}
}
}

The value this has is in handler cascading... If, for example,
make_readdable($file) failed too, then an exception handler around
handle_some_click could handle display a sudo box, to try it again,
and eventually continue back into the make_readable($file) call,
which will then continue into the failed open.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &
/\ kung foo master: /me tips over a cow: neeyah!!!!!!!!!!!!!!!!!!!!!!

Yuval Kogman

unread,
Sep 25, 2005, 11:11:22 AM9/25/05
to perl6-l...@perl.org
In order to support continuable exception generators, here is a
style guide for exception values, and an observation on what
exceptions should support at the language level:

* Exceptions should preserve data

All exceptions should contain as much of the original data that
caused them...

With Luke's new theory proposal, storing the argument tuple for the
call the raised the exception is logical. For example, for a
Permission Denied error raised in open() we should have access to
the filename that we tried to open, and the mode we tried to open it
with.

* Each thrown exception should contain the value of $! from before
it was thrown

* Each thrown exception has to know whether it was thrown because of
'use fatal' or because of an explicit 'die'

* fail should be used except for very strict conditions

* use fatal should be encouraged by the documentation, for one
liners, scripts, and full blown apps.

* Context data should be overridable at the exception object level

Carp and friends should be implemented not by using a different
keyword than fail, but failing with a value that specifies a certain
context.

sub croak (*$a) { # tuples again
fail Exception.new(*$a, :position($?CALLER::CALLER::POSITION));
}

* fail should cascade such that every time a block is left with a
failing return value, the block's caller should be checked for use
fatal.

* When continuing into an exception, the parameters to the
continuation are substituted for the error.

use fatal;

my $value = {
fail "foo";

CATCH {
$!.continue("bar");
}
}

say $bar;

Continuing into the exception with no arguments is like ignoring
'use fatal' for this specific exception, causing the failed value to
propagate out of the fatalized block.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me wields bonsai kittens: neeyah!!!!!!!!!!!!!!!!

Yuval Kogman

unread,
Sep 25, 2005, 11:38:04 AM9/25/05
to perl6-l...@perl.org
To ease the understanding of exactly how this might work, assume
that perl 6 is really all continuation passing style under the
surface (parrot is ;-).

use fatal;
my $x = do_bar();
do_foo();

sub do_bar {
fail "bah";
}

The way CPS works is really simple. The principal is that there is
no concept of return. Instead, the call to do_bar, for example, gets
an additional implicit parameter. This parameter is the code ref to
the "rest" of the code, and could look something like this if
deparsed:

sub ($rv) {
my $x = $rv;
do_foo()
}

So do_bar knows to make a goto to that code ref with it's return
value.

Now, fail gets something similar - it gets the code ref which
applies do_bar's passed in code ref, and it applies it the exception
constructed from "bah".

Fail will also check first to see if the continuation might, at some
level, use fatal. If it does, it takes a dynamically scoped value
instead - the continuation for the last encountered CATCH block.

When it throws the exception, fail will store the continuation it
got (the $x = ... stuff) in the exception object.

CATCH can then choose either to goto it's parent block's
continuation (exit normally), or to goto the continuation stored in
the exception object, which is indirectly the assignment to $x.

Autrijus's example makes this clear (he prefers "resume" over
"continue"):

use fatal;
my $x = { 1/0 * 3 CATCH { $!.resume(9) } }; # 27

What's happening is that &infix:</> checks if it's divisor is 0,
and if it is, it will fail. Fail has a continuation to 'multiply by
3 -> exit from block -> assign to $x', and it stores that in the
exception object. Instead of gotoing to that continuation, it will
instead take the last CATCH block that was relevant, in this case
the only one. This one finds the continuation to * 3 etc in the
exception object, and effectively augments the result of
infix:</>(1, 0) to be 9 instead of an undefined value.

I hope this makes things clear.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me does not drink tibetian laxative tea: neeyah!

Yuval Kogman

unread,
Sep 25, 2005, 11:27:07 AM9/25/05
to perl6-l...@perl.org
On Sun, Sep 25, 2005 at 18:11:22 +0300, Yuval Kogman wrote:
> In order to support continuable exception generators, here is a
> style guide for exception values, and an observation on what
> exceptions should support at the language level:

And more...

* Exception strings are for humans

Humans need to know what happened in their own words. They have a
hard time analyzing error codes, or constant names.

Computers have a hard time analyzing strings, and prefer hard data.

It should be easy to identify each error in a way that is meaningful
to computers, and this way should be orthogonal to the string that
is displayed to the user when possible.

This means that it should be incredibly easy for the user to declare
exception classes as they are used, perhaps by stealing goodness
from enumish constructs, or somesuch.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: *shu*rik*en*sh*u*rik*en*s*hur*i*ke*n*: neeyah!!!!

Luke Palmer

unread,
Sep 25, 2005, 1:32:54 PM9/25/05
to Yuval Kogman, perl6-l...@perl.org
On 9/25/05, Yuval Kogman <nothi...@woobling.org> wrote:
> I propose a new model - each exception has a continuation that
> allows it to be unfatalized.

I think we've already talked about something like this. But in the
presence of "use fatal", it makes a lot more sense.

Something comes to mind:

use fatal;
sub foo() { bar() }
sub bar() { baz() }
sub baz() { quux() }
sub quux() { fail }
{
say foo();
CATCH { $!.continue(42) }
}

Exactly which exception is continued? Where do we cut off the call
chain and replace our own value? This comes up again with open().
Let's say open is implemented with a series of five nested calls, the
innermost which knows how to fail and propagate outwards. However,
the programmer using open() has no idea of its internals, so it ought
to override the return value of open() itself, rather than its utility
functions. However, we can't go with outermost, because then you'd
only be "fixing" the lexical call ("say foo()" above). So it's
somewhere in between. Where?

Luke

Michael Walter

unread,
Sep 25, 2005, 2:15:40 PM9/25/05
to Luke Palmer, Yuval Kogman, perl6-l...@perl.org
On 9/25/05, Luke Palmer <lrpa...@gmail.com> wrote:
> [...]

> Exactly which exception is continued?
> [...]
Named restarts in Common Lisp appear to try to solve a related
problem, if I'm skimming this thread correctly. :-) (see [1]).

Michael

[1] http://www.supelec.fr/docs/cltl/clm/node312.html#SECTION003300000000000000000

Yuval Kogman

unread,
Sep 25, 2005, 2:45:05 PM9/25/05
to Luke Palmer, perl6-l...@perl.org
On Sun, Sep 25, 2005 at 11:32:54 -0600, Luke Palmer wrote:

> Exactly which exception is continued?

...
> This comes up again with open(). So it's somewhere in between.
> Where?

For the open() example I don't have a solution but I know in what
direction to throw it: the same thing that makes croak appear to
work from the caller =)

Several things come to mind... First is this: (ugly)

sub open {
...
CATCH { fail $! }; # force it to go up
}

This is the most flexible, but also the crudest method.

Another idea is to use the same namespace hopping logic that Carp
does right now. This doesn't work for exceptions in the same class
so it should be optional and off by default... Perhaps:

fail "error" :package_boundry; # better word needed

Another idea is to use a lexical pragme that delays all errors,
which is like CATCH { fail $! } but more declarative:

sub open {
use fatal <propagate>;
...
}

For when it isn't specified, when doing evil things such as stepping
into the continuation of an exception, we assume that the code doing
the CATCH knows about the code that raised the exception at least
something.

Encapsulation is sort of maintained by specifying what kinds of
operations are fixed.. For example, errors having to do with opening
files are fixuppable by taking the arguments to open found in the
exception, and trying to reopen the file after some fudging, and
returning a handle. This is common knowlege about the interface of
open, and we are simply saying that for the rest of the dynamic
scope (dangerous, perhaps) we are trying to fixup every call to open
that fails with this handler.

This is no different than catching that exception in terms of
encapsulation, except that the failure is defatalized. My claim is
that just as you know the kind of error it is when you explicitly
catch it for the purpose of reporting, you have the same knowlege
when you are fixing.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me sushi-spin-kicks : neeyah!!!!!!!!!!!!!!!!!!!!

Piers Cawley

unread,
Sep 26, 2005, 12:40:52 PM9/26/05
to Luke Palmer, Yuval Kogman, perl6-l...@perl.org
Luke Palmer <lrpa...@gmail.com> writes:

> On 9/25/05, Yuval Kogman <nothi...@woobling.org> wrote:
>> I propose a new model - each exception has a continuation that
>> allows it to be unfatalized.
>
> I think we've already talked about something like this. But in the
> presence of "use fatal", it makes a lot more sense.
>
> Something comes to mind:
>
> use fatal;
> sub foo() { bar() }
> sub bar() { baz() }
> sub baz() { quux() }
> sub quux() { fail }
> {
> say foo();
> CATCH { $!.continue(42) }
> }
>
> Exactly which exception is continued?

The bottommost one. If you want to return to somewhere up its call chain, do:

$!.caller(n).continue(42)

Assuming caller returns a continuation (which I still fondly hope it will). I'm
assuming you're examples aren't necessarily tail calls of course.


> Where do we cut off the call chain and replace our own value? This comes up
> again with open(). Let's say open is implemented with a series of five
> nested calls, the innermost which knows how to fail and propagate outwards.
> However, the programmer using open() has no idea of its internals, so it
> ought to override the return value of open() itself, rather than its utility
> functions. However, we can't go with outermost, because then you'd only be
> "fixing" the lexical call ("say foo()" above). So it's somewhere in between.
> Where?

Obviously the topmost function should do:

sub open(...) {
...
CATCH { $!.rethrow }
}

This assumes that 'rethrow' throws a new exception that delegates to the
original exception for lots of its behaviour. If, later, you want to explicitly
get at the exception thrown by the helper function, you could do something
like:

$!.inner

--
Piers Cawley <pdca...@bofh.org.uk>
http://www.bofh.org.uk/

TSa

unread,
Sep 26, 2005, 2:17:05 PM9/26/05
to perl6-l...@perl.org
HaloO,

Piers Cawley wrote:
>>Exactly which exception is continued?
> The bottommost one. If you want to return to somewhere up its call chain, do:
>
> $!.caller(n).continue(42)

Whow, how does a higher level exception catcher *in general* know
what type it should return and how to construct it. The inocent foo()
caller shouldn't bother about a quux() somewhere down the line of
command. Much less of it innards.

Think of receiving a 'shelf picker died of lung cancer' exception
when you just ordered a book from your favorite book dealer. Irrespective
of the seriousness to the shelf picker, but how would you expect a customer
to handle such an exception?

To me exceptions are to be handled with respect to the service
the scope invoked. In my example I would conclude that the book dealer has
more than one shelf picker under contract and just re-issue the order.
Well, or buy it elsewhere. In other words if the task at hand is
buying a certain book, exception handling means iterating all viable
sources with the assumption that the most convenient one *usually* delivers.
Otherwise I would code an iteration over the sources right away.

And handling user errors in a GUI application is a task for event handling,
*not* exception handling. I agree that both mechanisms share large parts
of the infra-structure supporting them. But I make a strong conceptual
distinction between them.

Which leads to the question, does Perl6 have or need a build-in event
system on the language level?
--
$TSa.greeting := "HaloO"; # mind the echo!

Piers Cawley

unread,
Sep 26, 2005, 7:05:53 PM9/26/05
to TSa, perl6-l...@perl.org
TSa <Thomas....@orthogon.com> writes:

> HaloO,
>
> Piers Cawley wrote:
>>>Exactly which exception is continued?
>> The bottommost one. If you want to return to somewhere up its call chain, do:
>> $!.caller(n).continue(42)
>
> Whow, how does a higher level exception catcher *in general* know
> what type it should return and how to construct it.

It asks the continuation? The information should be there. Consider a function
definition:

sub whatever(...) returns List {
throw ResumableException;
}

Then, assuming that caller returns continuation -- which is a pretty big
assumption, but which would be really cool -- $!.caller(1) would be a
continuation with an signature of (List $not_a_real_name). If the function can
return several types dependent on context, then the continuation's signature
would be the appropriate one for the context in which the function was
called. Monkeying with this kind of thing in code isn't necessarily a good
idea, but it's great for, for instance, having a top level exception catcher
that could (potentially) bring up an interactive shell with limited debugging
features which would allow you to inspect the running program and work out what
went wrong. Ruby on Rails already does something like this, with the added
wrinkle that, if it hits a breakpoint when running in the server it hands off
the interactive session to a console process rather than having you monkey with
it within your browser. A very neat trick and a remarkably powerful debugging
technique.


> The inocent foo() caller shouldn't bother about a quux() somewhere down the
> line of command. Much less of it innards.
>
> Think of receiving a 'shelf picker died of lung cancer' exception when you
> just ordered a book from your favorite book dealer. Irrespective of the
> seriousness to the shelf picker, but how would you expect a customer to
> handle such an exception?

I wouldn't, but I would expect that a programmer would find such an exception
very useful indeed.

Yuval Kogman

unread,
Sep 26, 2005, 7:20:14 PM9/26/05
to Piers Cawley, Luke Palmer, perl6-l...@perl.org
On Mon, Sep 26, 2005 at 17:40:52 +0100, Piers Cawley wrote:
> Luke Palmer <lrpa...@gmail.com> writes:
>
> > On 9/25/05, Yuval Kogman <nothi...@woobling.org> wrote:
> >> I propose a new model - each exception has a continuation that
> >> allows it to be unfatalized.
> >
> > I think we've already talked about something like this. But in the
> > presence of "use fatal", it makes a lot more sense.
> >
> > Something comes to mind:
> >
> > use fatal;
> > sub foo() { bar() }
> > sub bar() { baz() }
> > sub baz() { quux() }
> > sub quux() { fail }
> > {
> > say foo();
> > CATCH { $!.continue(42) }
> > }
> >
> > Exactly which exception is continued?
>
> The bottommost one. If you want to return to somewhere up its call chain, do:
>
> $!.caller(n).continue(42)

This breaks encapsulation, like luqui mentioned.

However, since every exception has an exception stack, i guess you
could see exactly how it was propagated non-fatally, before it was
actually thrown.

> sub open(...) {
> ...
> CATCH { $!.rethrow }
> }

...
> $!.inner


That way behavior like that could be automated

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me sushi-spin-kicks : neeyah!!!!!!!!!!!!!!!!!!!!

Nigel Hamilton

unread,
Sep 27, 2005, 2:29:48 AM9/27/05
to TSa, perl6-l...@perl.org
> And handling user errors in a GUI application is a task for event handling,
> *not* exception handling. I agree that both mechanisms share large parts
> of the infra-structure supporting them. But I make a strong conceptual
> distinction between them.
>
> Which leads to the question, does Perl6 have or need a build-in event
> system on the language level?
> --

Hear, hear.

Isn't just an exception a naughty event? But when you think about it,
isn't all programming about suggesting events and handling what happens
next?

Most of what a programmer does is suggest events to do (e.g., open file)
and then handle the 'events' that happen next (e.g., file not found) and
then suggest some more events (e.g., die). At the moment the programmer is
the top-level event handler (e.g., damn - why didn't that work?) - but
maybe some of this hassle could be handed off?

Is there a way to turn the exceptions type matching / depth finding
problem inside out?

From the point of view of the operating system a program is a nasty
exception to its normal running environment - your whole program is a kind
of big exception!

Like someone intruding on a conversation the programmer tries to change
the topic of the operating system. "Hey can you get that network socket
for me?"

Programmers are constantly trying to change the "topic" of conversation of
the operating system. "Hey drop what you're doing. This is important. Get
me that file."

But some of the nastiest (and exception raising) problems arise from the
conversational disconnect between operating system and program (e.g.,
"Sorry that port is blocked", "Sorry can't find that file", "Socket
closed" etc).

Is there a place in the room for a third party to the conversation?
Somebody who could ...

Smooth over misunderstandings?
Do translation if either party doesn't understand?
Queue what should be the next topic of conversation?
Remember how topics are meant to be handled?
Provide a log of what was said?

Is there a way of integrating Perl's natural ability to stay on topic ($_)
with exception handling, and event handling?

Just some thoughts ...

NIge

Yuval Kogman

unread,
Sep 27, 2005, 6:46:42 AM9/27/05
to Nigel Hamilton, TSa, perl6-l...@perl.org
On Tue, Sep 27, 2005 at 07:29:48 +0100, Nigel Hamilton wrote:
> >And handling user errors in a GUI application is a task for event handling,
> >*not* exception handling. I agree that both mechanisms share large parts
> >of the infra-structure supporting them. But I make a strong conceptual distinction between
> >them.
> >Which leads to the question, does Perl6 have or need a build-in event
> >system on the language level?

The exception mechanism is an event system that is typically used
for nothing but errors, but is much more extensible than that.

In fact, exceptions are nothing more than continuations really.

The CATCH block 'temp' sets a global exception handler continuation,
and die is simply

sub die (*$a) { &*EXCEPTION_HANDLER.(*$@) }

Also, it's hard for a library writer to consistently decide what is
an error and what isn't. It's also too much trouble. If every
library had severity levels, things would be complicated for small
scripts.

On the other hand, whenever an event that is not what 90% of the
flow control should be does occur, exceptions let you delegate the
behavior of how to deal with this to other code, specifically the
code that called you.

It just happens that the default &*EXCEPTION_HANDLER continuation is
essentially

print STDERR "@_";
exit $bad_error_code;

> Hear, hear.
>
> Isn't just an exception a naughty event? But when you think about it, isn't all programming
> about suggesting events and handling what happens next?

Well, not necessarily. It's an event that is exceptional, i.e. not
normal.

99% of non normal events are errors, but we routinely use exceptions
for timeout, etc.

{
temp $SIG{ALRM} = { die "timeout"; } # we need a better mechanism for this in perl 6, IMHO

alarm 10;
connect(...);

CATCH {
when "timeout" {
$!.resume if prompt("The remote party does not seem to be responding. Keep trying?");
}

default { $!.rethrow }
}
}

Timing out is not an error unless the user decides it is. Normally
the user would have to decide beforehand when lack of responsiveness
becomes an error, but with a system like this the user can be
prompted on a case by case basis to decide whether it's an error or
not.

> Is there a way of integrating Perl's natural ability to stay on topic ($_) with exception
> handling, and event handling?

As I see it continuations are a way to get back on topic...

Hey, 3rd party, the underlying thingy told me that yadda
yadda... If you can fix it, here's what you do next.

This is why I proposed the idea.

I would like to expand on your ideas though - the program is layered
with a delegation hierarchy... The lowest level agent is the
operating system. On the other side, the user makes a query to the
program which makes a query to it's first level handler, that uses a
library, that uses another and so on and so forth.

In a sense, the third party you mention is always the caller of a
given block of code, with the other two parties being the called
level, and the level below it.

Intervention on behalf of the third party can be delegated upwards
if this layer does not have enough policy on event handling. The
only level which really knows how to behave under any event
(hopefully ;-) is the user.

> Just some thoughts ...

Good post!

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me does a karate-chop-flip: neeyah!!!!!!!!!!!!!!

Ruud H.G. van Tol

unread,
Sep 27, 2005, 11:53:49 AM9/27/05
to perl6-l...@perl.org
Nigel Hamilton schreef:

> From the point of view of the operating system a program is a nasty
> exception to its normal running environment - your whole program is a
> kind of big exception!

As if a real OS really likes to run idle most of the time.
;)


> Like someone intruding on a conversation the programmer tries to
> change the topic of the operating system. "Hey can you get that
> network socket for me?"
>
> Programmers are constantly trying to change the "topic" of
> conversation of the operating system. "Hey drop what you're doing.
> This is important. Get me that file."

Or the OS is more like a waiter, nervously circling the tables, watching
out for orders.


> Is there a way of integrating Perl's natural ability to stay on topic
> ($_) with exception handling, and event handling?

http://poe.perl.org/?POE_FAQ/Will_Perl_6_make_POE_obsolete


And how about agents and messages?

--
Grtz, Ruud

Larry Wall

unread,
Sep 28, 2005, 12:49:11 PM9/28/05
to perl6-l...@perl.org
I'd like to add a little more. The context of the original throw
probably knows best whether it's even possible or sensical to
continue, so it should be optional whether the exception contains a
resume continuation. The presence of the continuation signals that
the inner code would *like* to continue but can't figure out how
without more dynamic context, where that could even include human
interaction on some level. The absence of a resume continuation
means someone thinks this dynamic branch needs a wooden stake through
its heart.

But thinking about optional continuations, another thing occured
to me. It's always bugged me that warnings were something different
from exceptions, and now I think we can unify them, if we say that
a warning is simply an exception with two properties. The first is
that it's in a category that, by default, the outermost runtime will
just report on and resume. And the second is, of course, that the
resume continuation is required. So turning any warning into a
fatal error consists of stripping the resume continuation. (Which
might mean that the presence of the continuation is what differentiates
warnings from fatal errors, but I doubt it. There needs to be a
class of exceptions that have a resume continuation that is not resumed
by default. A "die" should throw that kind of exception, and have
a reasonably expectation of not being resumed in the normal course of
things. Nevertheless, a "die" is well formed enough that we know where
it should resume if it forced into a warning instead.)

This gives us the ability to capture any warning in the dynamic
scope and do something about it. (Whether it should by default
pop up in a CATCH block or a CONTROL block, or have a WARN block
of its own, I don't know.) But this gives us dynamic control over
warnings to complement (not replace) the lexical control provided by
"use warnings".

Larry

Yuval Kogman

unread,
Sep 28, 2005, 6:18:14 PM9/28/05
to perl6-l...@perl.org
On Wed, Sep 28, 2005 at 09:49:11 -0700, Larry Wall wrote:
> I'd like to add a little more. The context of the original throw
> probably knows best whether it's even possible or sensical to
> continue, so it should be optional whether the exception contains a
> resume continuation. The presence of the continuation signals that
> the inner code would *like* to continue but can't figure out how
> without more dynamic context, where that could even include human
> interaction on some level. The absence of a resume continuation
> means someone thinks this dynamic branch needs a wooden stake through
> its heart.
>
> But thinking about optional continuations, another thing occured
> to me. It's always bugged me that warnings were something different
> from exceptions, and now I think we can unify them, if we say that
> a warning is simply an exception with two properties. The first is
> that it's in a category that, by default, the outermost runtime will
> just report on and resume. And the second is, of course, that the
> resume continuation is required. So turning any warning into a
> fatal error consists of stripping the resume continuation. (Which
> might mean that the presence of the continuation is what differentiates
> warnings from fatal errors, but I doubt it. There needs to be a
> class of exceptions that have a resume continuation that is not resumed
> by default. A "die" should throw that kind of exception, and have
> a reasonably expectation of not being resumed in the normal course of
> things.

So CATCH does set the &*EXCEPTION_HANDLER continuation to be something
like:

given $! {
when Exception::ResumesByDefault {
old_exception_handler($!);
}

... # the body of the CATCH
}

and

> or have a WARN block of its own, I don't know.

Or maybe &*EXCEPTION_HANDLER is a multi-method-continuation.

CATCH could simply be a macro that compiles to

ENTER {
temp &*EXCEPTION_HANDLER := &*OUTER::EXCEPTION_HANDLER;
&EXCEPTION_HANDLER.add_variant(cont (Exception::Fatal $!) {

};
}

BTW, how do we declare continuations explicitly? Or is it just a sub
that is directly .goto'd ?

An MMD exception handler that is extended in the dynamic scope is
cool because it's not limitied to just control exceptions, warnings,
and fatal errors.

Some fun definitions:

every loop construct creates some lexical bindings in the body:

&next - throws an Exception::Control::Next
&redo - throws an Exception::Control::Redo
...

Entering the loop adds temporary variants to
&*EXCEPTION_HANDLER:

multi cont (Exception::Control::Redo) {
body.goto(*$current_binding_tuple);
}

multi cont (Exception::Control::Next) {
...
}

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me climbs a brick wall with his fingers: neeyah!

TSa

unread,
Sep 29, 2005, 4:49:40 AM9/29/05
to perl6-l...@perl.org
HaloO,

Yuval Kogman wrote:
> On Wed, Sep 28, 2005 at 09:49:11 -0700, Larry Wall wrote:
>>But thinking about optional continuations, another thing occured
>>to me. It's always bugged me that warnings were something different
>>from exceptions, and now I think we can unify them, if we say that

Yes, I'm also all for unifying the concepts. But please
don't let us call it exception. Exception should be a
termination oriented (sub)concept. Some kind of scoped giving
up. E.g. a traffic accident is the exceptual case that
stops the regular traffic in a certain area. But for
medics, firemen and police they are the usual case up to
a limit where it also becomes exceptional to them.

Take the example of firemen. They are some kind of installed
exception handlers. And they can handle flooded basements
but New Orleans and Bavaria have thrown a too big exception.
Well, or take the engineers of the Titanic, they trusted their installed
exception handlers to cope with a breached hull so much that they
didn't install enough rescue boats.

BTW, I would call *intentional* exceptions terrorism.

>>a warning is simply an exception with two properties.

>>or have a WARN block of its own, I don't know.
>
>
> Or maybe &*EXCEPTION_HANDLER is a multi-method-continuation.

> ...


> An MMD exception handler that is extended in the dynamic scope is
> cool because it's not limitied to just control exceptions, warnings,
> and fatal errors.
>
> Some fun definitions:

In lack of a better word I use Event and we get
Event::Exception, Event::Control, Event::Warn and
possibly Event::Hint, Event::Log and Event::Fun :)

The only drawback of choosing 'Event' might be that it
smells too GUIish for some? Or is that a feature? That is
we get Event::GUI::press, Event::GUI::scroll, Event::GUI::expose,
etc.

Yuval Kogman

unread,
Sep 29, 2005, 5:25:01 AM9/29/05
to TSa, perl6-l...@perl.org
Hi!

On Thu, Sep 29, 2005 at 10:49:40 +0200, TSa wrote:
> BTW, I would call *intentional* exceptions terrorism.

Then I would call terrorism non linear control flow ;-)

In that case Afghanistan might be harboring computer scientists that
really like CPS, and bush is Java ;-)

> In lack of a better word I use Event and we get
> Event::Exception, Event::Control, Event::Warn and
> possibly Event::Hint, Event::Log and Event::Fun :)

An event is something external that happens... It does not affect
the control flow of an application.

Exceptions work well to describe this:

The normal flow is up and down the call graph, laying bread crumbs
in the form of a stack, and going back from where you came.

Exceptions let you jump instantly to another place in the call
graph. All I'm saying is that to complement the normal "exceptions
jump back out", you can jump out, and jump back in.

An event is a general thing that can be implemented with exception
like semantics, but implies nothing on the control flow of the
program.

An error is an exception that happenned because something went badly
wrong.


Now it's my turn at illustrative metaphors ;-)

The call graph is a big maze you go through.

When you find an *error*, in the form of a minotaur, you use the
exception handling mechanism to run away.

You jump instantly to a safe place in the labyrinth.

Although this is not consistent with the mythology, presume that the
reason you entered the maze was that you were trying to get results.

Most exception handlers are safe places when you can gather your
self, realize that 'oh crap, i just met a minotaur', and tell
whoever you sent you in there that there's no way you're going back
in.

All I'm trying to say is that when there is an exception - a leap
out from a dangerous place to a safe one, due to an error, the code
may choose to deal with the error by giving you a big stick. Then
you can go back and beat the minotaur into submission, and resume
with trying to get results.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me whallops greyface with a fnord: neeyah!!!!!!!

Yuval Kogman

unread,
Sep 29, 2005, 5:38:57 AM9/29/05
to TSa, perl6-l...@perl.org
I'd like to ammend, and perhaps formalize with some definitions from
my dictionary, which ships with OSX:

error - a mistake... the state or condition of being wrong in
conduct or judgement... technical - a measure of the estimated
difference between the observed or calculated value of a
quantity and it's true value.

As i see it:

when something you wanted to happen turned out different than
you programmed it to:

my $handle = open "file";
# we assume $handle exists
print =<$handle>; # but what if there was an error?

exception - a person or thing that is excluded from a general
statement or does not follow a rule

To lessen the mental load on a programmer, instead of having the
programmer write a tree of all the conditions that could happen, the
programmer can write only the condition in which the program is
actually useful.

Any *error* (synch problems between the code and reality) causes an
*exception* in this linearization. The control flow is an exception
to the norm, because there was an exception in the reality the
program was tailored to deal with.

The reason we handle exceptions is that sometimes we want the tree
approach, because we have well defined behavior for certain paths.
Exceptions let us separate code from "normal" code and code which is
*exceptional*, and the reason it is exceptional is usually an error.

event - a thing that happens, especially one of importance

Every error is an event. Exceptions are one way to deal with events
we were not prepared for. But events can also be waited for (select
waits for events on many file handles at a time).

Every *error* is an *event*, but an *exception* is how you deal with
events. The events that cause exceptions by default in perl will be:

errors, when 'use fatal' is in effect

warnings, but to another handler

next, redo, last, return - all control flow events that are
exceptions to the single point of entry, single point of exit
school of thought.

I intentionally did not use the computer related definitions from
e.g. wikipedia, because they are more subject to cultural inertia,
and we are trying to discover the roots of these borrowed terms.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me groks YAML like the grasshopper: neeyah!!!!!!

Ruud H.G. van Tol

unread,
Sep 29, 2005, 8:09:13 AM9/29/05
to perl6-l...@perl.org
TSa schreef:

> Yes, I'm also all for unifying the concepts. But please
> don't let us call it exception. Exception should be a
> termination oriented (sub)concept. Some kind of scoped giving

> up. [...]


> In lack of a better word I use Event and we get
> Event::Exception, Event::Control, Event::Warn and
> possibly Event::Hint, Event::Log and Event::Fun :)
>
> The only drawback of choosing 'Event' might be that it
> smells too GUIish for some? Or is that a feature? That is
> we get Event::GUI::press, Event::GUI::scroll, Event::GUI::expose,
> etc.


FSM etc.
http://en.wikipedia.org/wiki/Event_driven_finite_state_machine

Running Perl code can be seen as an ATN (an FSM on steroids, says
http://smc.sourceforge.net/SmcFaq.htm).

An exception-handler can be allowed to pop more states than it is good
for.

--
Grtz, Ruud

Piers Cawley

unread,
Sep 30, 2005, 8:39:34 AM9/30/05
to TSa, perl6-l...@perl.org
TSa <Thomas....@orthogon.com> writes:
> BTW, I would call *intentional* exceptions terrorism.

So that would be all exceptions then. They all get implemented somewhere, even
the ones that get thrown by builtins.

CATCH Exception { say "Why do you hate freedom?" }

TSa

unread,
Sep 30, 2005, 12:02:46 PM9/30/05
to Piers Cawley, perl6-l...@perl.org
HaloO Piers,

you wrote:
> TSa <Thomas....@orthogon.com> writes:
>
>>BTW, I would call *intentional* exceptions terrorism.
>
>
> So that would be all exceptions then. They all get implemented somewhere, even
> the ones that get thrown by builtins.

I knew that the statement would emotionalize. Sorry to all who don't
like it an this list. But somehow I found it describes the impression
on the handling side somewhat. And I thought it illustrates that exceptions
shouldn't be considered just another tool.


>
> CATCH Exception { say "Why do you hate freedom?" }
>

I don't. But the freedom of the individual ends where the
community begins.

Yuval Kogman

unread,
Sep 30, 2005, 1:02:32 PM9/30/05
to TSa, Piers Cawley, perl6-l...@perl.org
On Fri, Sep 30, 2005 at 18:02:46 +0200, TSa wrote:
> I knew that the statement would emotionalize. Sorry to all who don't
> like it an this list. But somehow I found it describes the impression
> on the handling side somewhat. And I thought it illustrates that exceptions
> shouldn't be considered just another tool.

I think you're taking it too seriously. I'm 99% sure Piers was
joking.

Regardless, exceptions *are* just another tool. They let you write
safe code in fewer words and with less distraction.

For example, Either you linearly serialize the entire tree of
possible events:

if (my $handle = open "file") {
# the handle is open
if (my $other_file = open "other" :w) {
for =<$handle> -> $line {
unless ($other_file.print($line)) {
$*ERR.print("other could not be written to: $!"); # disk might be full
if (close $other_file) {
if (close $handle) {
exit 1;
} else { ...; exit 1 }
} else { ...; exit 1 }
}
exit 0;
}
} else {
$*ERR.print("could not open other for writing: $!");
if (close $handle) {
exit 0;
} else {
$*ERR.print("could not close file: $!"); # not logical,
# since we don't write to file, but this is "safer"
exit 1;
}
}
} else {
print $*ERR, "could not open file: $!";
exit 1;
}

or you could throw exceptions:

use fatal;

my $handle = open "file";

my $other_file = open "other" :w;

for =<$handle> -> $line {
$other_file.print($line);
}

If you are going to avoid exceptions because they are too much
<something> for your taste, then I think you are misusing a language
that has support for exceptions.

I really don't understand why this has to do with freedom, or it's
restriction. It's your personal (and IMHO bad) taste not to use
exceptions for improving your code, but it's still your choice.

All I was saying is that you could leverage exceptions by letting
the UI code make the handling of exceptions a two way route, instead
of one way.

> > CATCH Exception { say "Why do you hate freedom?" }
>
> I don't. But the freedom of the individual ends where the
> community begins.

I think this is a big exaggeration. The community will not be harmed
if the individual uses exceptions.

On the contrary, i would be much happier to use code that does
through exceptions.

For example, a very useful perl 5 module, UNIVERSAL::require, lets
me write:

$class->require or die $UNIVERSAL::require::ERROR;

instead of

eval "require $class"; die $@ if $@;

but in both cases I have to check for errors, unlike

require Class;

I still prefer $class->require, though, because it feels more
readable to me. I don't say to myself "wtf? why is this code doing
an eval" while reading the code.

In perl 6, we would ideally have:

use fatal;
$class.require; # lives if class exists, dies if class doesn't exist
$class.method; # always lives (if method really exists)

or
use fatal;
try { $class.require } # always lives
$class.method; # might die, but at least it's obvious

or

no fatal;
$class.require; # always lives\
$class.method; # might die

In fact UNIVERSAL::require's author agrees with me:
http://use.perl.org/~schwern/journal/26939

Now, if this were

#!/usr/bin/perl

use fatal;
use Pluginish::App;

sub &infix:<s~> ($l, $r) { "$l $r" }; # spacey concatenator

{
Pluginish::App->load_plugins;
CATCH {
when Module::load_error {
if (prompt("The module $!.module_name could not be loaded because"
s~ "an error occurred ($!). Try to continue anyway?") {
$!.resume(undef);
} else {
die $!;
}
}
}

Pluginish::App->run;

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /methinks long and hard, and runs away: neeyah!!!

Peter Haworth

unread,
Oct 5, 2005, 11:57:51 AM10/5/05
to perl6-l...@perl.org
On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote:
> Piers Cawley wrote:
> >>Exactly which exception is continued?
> > The bottommost one. If you want to return to somewhere up its call
> > chain, do:
> >
> > $!.caller(n).continue(42)
>
> Whow, how does a higher level exception catcher *in general* know
> what type it should return and how to construct it? The innocent

> foo() caller shouldn't bother about a quux() somewhere down the line
> of command. Much less of its innards.

Well said.

> Think of receiving a 'shelf picker died of lung cancer' exception
> when you just ordered a book from your favorite book dealer.
> Irrespective of the seriousness to the shelf picker, but how would
> you expect a customer to handle such an exception?

This kind of exception should never get so far up the call chain,
except possibly as a nested cause of a more general exception. The
customer is in no position to get the book dealer to choose a
different stock picker - that's up to the order picking department of
the book shop. If it doesn't get handled there, then maybe the book
shop can try sourcing the book externally, and if that fails, they may
defer your order until later. The shop could ask the customer's
opinion as to the suitability of each of these options, but the
customer shouldn't have to know how the shop is implemented and make
such decision unprompted.

Even given a continuation to some low level routine, anything the
caller does is likely to be too far removed to make it sensible to
resume the continuation.

Eiffel deals with this by brutally its Design By Contract magic
bullet: Since all the code catching the exception knows about is its
own implementation, the only sane option it has is to try performing
the subtask in a different way. Here's how Eiffel's rescue/retry might
work in perl6, where "rescue" is spelled "CATCH".

class Picker{
# An individual order picker can pick a book, but may unfortunately
# die of lung cancer, or just fail to find the book
method pick($book){
fail Err::LungCancer if .is_smoker();
...
fail Err::BookNotFound if ...;
}
}

class PickingDept{
# The picking department employs pickers to pick books
# If an employee dies of lung cancer while picking an order,
# they are removed from the roster, and a different picker is chosen
# If the department doesn't have enough free pickers,
# it just throws a "too busy" exception
# Due to the retry semantics, the department will keep choosing pickers
# until one of them can pick the book, or they all die of lung cancer,
# (or they're all busy doing something else)
method pick($book){
my $picker=.find_free_picker();
$picker.pick($book);
...

CATCH{
when Err::NoPickersAvailable { fail Err::DeptTooBusy; }
when Err::LungCancer {
.fire_picker($picker); # Harsh, but we won't find him again
retry; # This re-runs the tried block (method) from the top
}
when Err::BookNotFound { fail Err::OutOfStock; } # Optimistic, maybe
default { fail; }
}
}
}

class BookStore{
# The book store has multiple picking departments, so if one of them fails
# to find a book, the others can be tried. If that fails, they could order
# the book from the wholesaler and defer the order, but I'm too lazy
method order($book){
for @.depts -> $dept {
try{
$dept.pick($book);
return;

CATCH{ 1; } # We'll just try the next one
}
}
fail Err::BookNotFound;
}

...
}

class Customer{
# The customer doesn't have any say in how bookshops are run, so all
# they can do if their order is refused, all they can do is try another
# shop, or give up and go home
method buy_book($book){
$random_bookshop.order($book);

CATCH{ fail Err::BookObviouslyDoesntExist; }
}
}

--
Peter Haworth p...@edison.ioppublishing.com
Hestons' First Law: I qualify virtually everything I say.

Yuval Kogman

unread,
Oct 5, 2005, 1:24:47 PM10/5/05
to Peter Haworth, perl6-l...@perl.org
On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote:
> On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote:
> > Piers Cawley wrote:
> > >>Exactly which exception is continued?
> > > The bottommost one. If you want to return to somewhere up its call
> > > chain, do:
> > >
> > > $!.caller(n).continue(42)
> >
> > Whow, how does a higher level exception catcher *in general* know
> > what type it should return and how to construct it? The innocent
> > foo() caller shouldn't bother about a quux() somewhere down the line
> > of command. Much less of its innards.
>
> Well said.


No! Not well said at all!

The exception handler knows *EVERYTHING* because it knows what
exception it caught:

CATCH {
when some_kind_of_error {
$!.continue($appropriate_value_for_some_kind_of_error)
}
}

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

Luke Palmer

unread,
Oct 5, 2005, 3:00:55 PM10/5/05
to Yuval Kogman, Peter Haworth, perl6-l...@perl.org
On 10/5/05, Yuval Kogman <nothi...@woobling.org> wrote:
> On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote:
> > On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote:
> > > Whow, how does a higher level exception catcher *in general* know
> > > what type it should return and how to construct it? The innocent
> > > foo() caller shouldn't bother about a quux() somewhere down the line
> > > of command. Much less of its innards.
> >
> > Well said.
>
>
> No! Not well said at all!
>
> The exception handler knows *EVERYTHING* because it knows what
> exception it caught:

I don't think it was a "how is this possible", but more of a "what
business does it have?". And as far as I gathered, they're saying
pretty much what you've been saying, but in a different way. It's
about the continuation boundary; that is, if you're outside a module,
you have no say in how the module does its business. You can continue
only at the module boundary, replacing a return value from its public
interface.

Of course, exactly how this "public interface" is declared is quite undefined.

Luke

Dave Whipp

unread,
Oct 5, 2005, 4:41:37 PM10/5/05
to perl6-l...@perl.org
Luke Palmer wrote:
> Of course, exactly how this "public interface" is declared is quite undefined.

Reading this thread, I find myself wondering how a resumable exception
differs from a dynamically scropted function. Imagine this code:

sub FileNotWriteable( Str $filename ) {
die "can't write file: $filename";
}

sub write_file (Str $filename) {
FileNotWriteable($filename) unless -w $filename;
...
}


sub my_program {

temp sub FileNotWriteable( Str $filename ) {
return if chmod "+w", $filename;
OUTER::FileNotWriteable( $filename );
}

...
write_file("foo.txt");
...
}


Ignoring syntactic sugar, what semantics does exception handling have
that a dynamically scoped function does not?

In the case of non-resumable exceptions, we see things like deferred
handling -- the exception is passed as a property of an undef value. I
assume such an exception cannot be resumed, so it does appear to me that
there are fundamental differences between resumable things, and
non-resumable, deferrable, exceptions. What is the benefit of unifying
them under a common syntax (CATCH blocks)?

Larry suggested that the exception mechanism might be a way of unifying
errors and warnings; but perhaps the opposite is true. Perhaps what we
see is a needed to generalize the distinction between warnigns and errors.


Dave.

Yuval Kogman

unread,
Oct 6, 2005, 6:36:46 AM10/6/05
to Luke Palmer, Peter Haworth, perl6-l...@perl.org
On Wed, Oct 05, 2005 at 13:00:55 -0600, Luke Palmer wrote:

> I don't think it was a "how is this possible", but more of a "what
> business does it have?". And as far as I gathered, they're saying
> pretty much what you've been saying, but in a different way. It's
> about the continuation boundary; that is, if you're outside a module,
> you have no say in how the module does its business. You can continue
> only at the module boundary, replacing a return value from its public
> interface.

As I see it this is the usefulness of exceptions being handled by
distant code.

The code in the module inverts it's interface by calling code it
doesn't know with a certain parameter, accepting a certain parameter
back.

That way encapsulation is not broken, but errors that happen deep
inside a call chain can be dealt with by code that can interact with
the user.

Yuval Kogman

unread,
Oct 6, 2005, 6:44:24 AM10/6/05
to Dave Whipp, perl6-l...@perl.org
On Wed, Oct 05, 2005 at 13:41:37 -0700, Dave Whipp wrote:
> Reading this thread, I find myself wondering how a resumable exception differs
> from a dynamically scropted function. Imagine this code:

This is sort of like what I mean, except that there is no
encapsulation breakage, since the interface is inverted.

The advantage of this approach is that error handling code in a
submodule can benefit from generic, reusable exception handling code
that is provided by it's caller.

> temp sub FileNotWriteable( Str $filename ) {

With an exception handler and continuable exceptions you don't have
to know what the error handler is, or make sure that the module
actually throws the error.

The exception handler instead deals with the type of the exception
in a generic manner (it doesn't care when the exception was actually
generated).

The module doesn't need to throw the error it just needs to fail (or
delegate a fail), until the failure crosses into a 'use fatal'
scope.

That way both the catching code and the throwing code are reusable
and orthogonal when they are unrelated, but the possibility of
coupling handling code with throwing code is still there.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

Peter Haworth

unread,
Oct 6, 2005, 1:11:38 PM10/6/05
to Yuval Kogman, perl6-l...@perl.org
On Wed, 5 Oct 2005 19:24:47 +0200, Yuval Kogman wrote:
> On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote:
> > On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote:
> > > Piers Cawley wrote:
> > > >>Exactly which exception is continued?
> > > > The bottommost one. If you want to return to somewhere up its call
> > > > chain, do:
> > > >
> > > > $!.caller(n).continue(42)
> > >
> > > Whow, how does a higher level exception catcher *in general* know
> > > what type it should return and how to construct it? The innocent
> > > foo() caller shouldn't bother about a quux() somewhere down the line
> > > of command. Much less of its innards.
> >
> > Well said.
>
> No! Not well said at all!

Sorry, I misread that. I thought I was agreeng with "how does a higher
level exception catcher know what to change in order to make resuming the
continuation useful?", especially in the light of Piers saying that the
bottom-most exception should be the one resumed.

The highest level exception is the only one a caller has any right to deal
with, but even then it doesn't really know what will happen if it resumes
some random continuation attached to the exception.

> CATCH {
> when some_kind_of_error {
> $!.continue($appropriate_value_for_some_kind_of_error)
> }
> }

That just gives me the willies, I'm afraid.

--
Peter Haworth p...@edison.ioppublishing.com
"Disconcerting Haworth Fortress Unicycling Melody Gundam"
-- http://www.channel4.com/4later/bits/manga.html

Yuval Kogman

unread,
Oct 6, 2005, 3:12:11 PM10/6/05
to Peter Haworth, perl6-l...@perl.org
On Thu, Oct 06, 2005 at 18:11:38 +0100, Peter Haworth wrote:

> The highest level exception is the only one a caller has any right to deal
> with, but even then it doesn't really know what will happen if it resumes
> some random continuation attached to the exception.

But then we gain nothing

> > CATCH {
> > when some_kind_of_error {
> > $!.continue($appropriate_value_for_some_kind_of_error)
> > }
> > }
>
> That just gives me the willies, I'm afraid.

Why?

when i can't open a file and $! tells me why i couldn't open, i
can resume with an alternative handle that is supposed to be the
same

when I can't reach a host I ask a user if they want to wait any
longer

when disk is full I ask the user if they want to write somewhere
else

when a file is unreadable i give the user the option to skip

These handlers are progressively more knowlegable of the code
they're dealing with, but they don't touch the actual guts - that's
the whole merit of continuable exception, because otherwise you
might aswell just start over.

These are 100% reusable in the first exception handler, and not
reusable but still applicable to an opaque call at the last handler.
It doesn't matter who opens the file file, the exception handler
will produce the same effect but in a different way.

CATCH {
when Error::IO::... {
open ...
}

when MyApp::Timeout {
ask_user_to_continue_or_abort();
}
...


}

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me dodges cabbages like macalypse log N: neeyah!

Luke Palmer

unread,
Oct 6, 2005, 4:27:30 PM10/6/05
to Yuval Kogman, Peter Haworth, perl6-l...@perl.org
On 10/6/05, Yuval Kogman <nothi...@woobling.org> wrote:
> when i can't open a file and $! tells me why i couldn't open, i
> can resume with an alternative handle that is supposed to be the
> same
>
> when I can't reach a host I ask a user if they want to wait any
> longer
>
> when disk is full I ask the user if they want to write somewhere
> else
>
> when a file is unreadable i give the user the option to skip

I'm not bashing your idea, because I think it has uses. But I'll
point out that all of these can be easily accompilshed by writing a
wrapper for open(). That would be the usual way to abstract this kind
of thing.

Luke

Yuval Kogman

unread,
Oct 6, 2005, 9:05:28 PM10/6/05
to Luke Palmer, Peter Haworth, perl6-l...@perl.org

Stylistically I would tend to disagree, actually. I think it's
cleaner to use exception handling for this.

Also, this implies that you know that the errors are generated by
open. This is OK for open(), but if the errors are generated from a
number of variants (MyApp::Timeout could come from anywhere in a
moderately sized MyApp), then wrapping is not really an option.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

Piers Cawley

unread,
Oct 7, 2005, 12:23:55 AM10/7/05
to Peter Haworth, Yuval Kogman, perl6-l...@perl.org
"Peter Haworth" <p...@edison.ioppublishing.com> writes:

> On Wed, 5 Oct 2005 19:24:47 +0200, Yuval Kogman wrote:
>> On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote:
>> > On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote:
>> > > Piers Cawley wrote:
>> > > >>Exactly which exception is continued?
>> > > > The bottommost one. If you want to return to somewhere up its call
>> > > > chain, do:
>> > > >
>> > > > $!.caller(n).continue(42)
>> > >
>> > > Whow, how does a higher level exception catcher *in general* know
>> > > what type it should return and how to construct it? The innocent
>> > > foo() caller shouldn't bother about a quux() somewhere down the line
>> > > of command. Much less of its innards.
>> >
>> > Well said.
>>
>> No! Not well said at all!
>
> Sorry, I misread that. I thought I was agreeng with "how does a higher
> level exception catcher know what to change in order to make resuming the
> continuation useful?", especially in the light of Piers saying that the
> bottom-most exception should be the one resumed.

I'm sorry, we appear to have lost some kind of context, the original example
given only had one exception thrown, but it got propagated up through a long
call chain. At no point did anything catch the original exception and
rethrow. If they had, you're absolutely correct in asserting that by default
things should resume from the point of the outermost rethrow. A brave exception
catcher (or more likely programmer with a debugger) might want to crack that
exception open and examine its inner exceptions, but in general that's not
going to be safe.

The scary syntax proposed above is, again, the sort of thing that might be
useful in a debugger "I don't really care about the inner workings of these
helper functions, I just want 'open' to return this mocked handle." (actually
in that case, being able to do $!.caller(&open).continue(MockIO.new), where
'caller &open' looks up the call chain for the lowest call to open and returns
that continuation would be rather neat)

> The highest level exception is the only one a caller has any right to deal
> with, but even then it doesn't really know what will happen if it resumes
> some random continuation attached to the exception.

Oh stop with the 'rights'. And it's not dealing with a 'random' continuation,
if it's going to resume it should be damned careful about which exceptions it
resumes from; you don't just go around doing CATCH {...; $!.continue(...)}, you
do CATCH SomeSpecificKindOfResumableException { ...; $!.continue(...)}. And, in
general, you don't do that either, because in the average program you catch the
exception at a point where you can simply return a sensible default to your
caller. Resumable exceptions come into their own, however, when you're
debugging. I can envisage doing:

perl6 -debug::on::error some_script

And have it run along happily until an exception gets propagated up to the top
level, at which point the debugger swings into action and uses the continuation
to tunnel back to the point at which the exception was thrown so the programmer
can inspect the program state, possibly fix things up, return something
sensible and carry on.

>> CATCH {
>> when some_kind_of_error {
>> $!.continue($appropriate_value_for_some_kind_of_error)
>> }
>> }
>
> That just gives me the willies, I'm afraid.

It doesn't amuse me that much, unless whatever generates
$appropriate_value_for_some_kind_of_error is very, very smart indeed. But, as
I've said above, that's not really where resumable exceptions shine.

Austin Hastings

unread,
Oct 7, 2005, 2:31:12 AM10/7/05
to Yuval Kogman, Luke Palmer, perl6-l...@perl.org
Yuval Kogman wrote:

>On Thu, Oct 06, 2005 at 14:27:30 -0600, Luke Palmer wrote:
>
>
>>On 10/6/05, Yuval Kogman <nothi...@woobling.org> wrote:
>>
>>
>>> when i can't open a file and $! tells me why i couldn't open, i
>>> can resume with an alternative handle that is supposed to be the
>>> same
>>>
>>> when I can't reach a host I ask a user if they want to wait any
>>> longer
>>>
>>> when disk is full I ask the user if they want to write somewhere
>>> else
>>>
>>> when a file is unreadable i give the user the option to skip
>>>
>>>
>>I'm not bashing your idea, because I think it has uses. But I'll
>>point out that all of these can be easily accompilshed by writing a
>>wrapper for open(). That would be the usual way to abstract this kind
>>of thing.
>>
>>
>
>
>

Writing a "wrapper" may be the implementation mechanics:

sub safe_open()
{
call;
CATCH { when E::AccessDenied { return show_user_setuid_dialog(); }}
}

&open.wrap(safe_open);

But this is just one way to do it, and it fails to provide for "helping"
other people's code: Yuval's GUI environment would offer to fix the
problem for ALL file related calls (open, dup, popen, ad nauseum), and
would not have to worry about the order in which calls are wrapped. But
see below.

>Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this.
>
>Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option.
>
>

I think that what your proposal *really* requires is uniformity. There
are other ways to get the same behavior, including an abstract factory
interface for exception construction (would provide "virtual
constructor" for exceptions, so permitting userspace to insert a 'retry'
behavior), but it has the same vulnerability: the p6core must cooperate
in uniformly using the same mechanism to report errors: throw, fail,
die, error, abend, whatever it's eventually called.

sub *::throw(...)
{
return recover_from_error(*@_)
or P6CORE::throw(*@_);
}

=Austin

Yuval Kogman

unread,
Oct 7, 2005, 6:29:45 AM10/7/05
to Austin Hastings, Luke Palmer, perl6-l...@perl.org

We have:

die: throw immediately

fail: return an unthrown exception, which will be thrown
depending on whether our caller, and their caller - every scope
into which this value propagates - is using fatal.

This is enough for normal exception handling.

As for recovery - the way it's done can be specialized on top of
continuations, but a continuation for the code that would run had
the exception not been raised is the bare metal support we need to
do this.

Where this gets taken further by (IMHO overly abstract) exception
handling libraries and standardization is a question that
application development kits (e.g. Catalyst, GTK, Cocoa) must
develop a policy for.

> return recover_from_error(*@_)

what does this do?

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me beats up some cheese: neeyah!!!!!!!!!!!!!!!!!

Yuval Kogman

unread,
Oct 7, 2005, 6:34:11 AM10/7/05
to Piers Cawley, Peter Haworth, perl6-l...@perl.org

It doesn't really matter since 'fail'ed exceptions will simply be
converted to a "return with the continued value" when resumed, and
the question of outer/inner scopes is really irrelevant - it's like
tail calls.

As for die - since there is no implicit returning in die, it might
or might not make sense to try and resume. However, for most
situations it still looks to me like 'die "foo"' could be treated as
return in a way.

Essentially throwing an error means that the function/method who
threw it is giving up since it doesn't know how to resume.

If the exception handler can find an alternative for that function,
and replace the value, it's immediate caller should get the fixed
value, since they are the ones who need the value.

Functions which delegate exceptions are, again, just like tail
calls.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /me wields bonsai kittens: neeyah!!!!!!!!!!!!!!!!

Miroslav Silovic

unread,
Oct 7, 2005, 7:00:14 AM10/7/05
to lrpa...@gmail.com, nothi...@woobling.org, p...@edison.ioppublishing.com, perl6-l...@perl.org
lrpa...@gmail.com wrote:

>I'm not bashing your idea, because I think it has uses. But I'll
>point out that all of these can be easily accompilshed by writing a
>wrapper for open(). That would be the usual way to abstract this kind
>of thing.
>
>
>

My take on this: resumable exceptions break encapsulation no more (and
no less) than using callbacks. The function that throws a resumable
exception can only do this knowingly, and it could just as well offer a
callback for that specific patchable error. So why take away a
potentially highly useful tool?

BTW, two languages I know of have resumable exceptions: Common LISP and
TOM. Neither supports continuations, instead, they separate raising
exception (which runs a handler; this is implemented as a callback) from
unwinding stack (which happens when the handler actually throws; it can
choose to return instead, resuming execution if the raising function is
okay with it). At least in Common LISP this is used a *lot* during
interactive development, as it allows the developer to quickly patch
things up without reruning the failed test. Assuming perl6 keeps the
pugs-style interactive shell, I suspect resumable exceptions will be
quickly added into the standard library if they don't pass into the
standard. Yes, they're that useful.

Miro


TSa

unread,
Oct 7, 2005, 1:05:14 PM10/7/05
to perl6-l...@perl.org
HaloO

Yuval Kogman wrote:
> We have:
>
> die: throw immediately
>
> fail: return an unthrown exception, which will be thrown
> depending on whether our caller, and their caller - every scope
> into which this value propagates - is using fatal.
>
> This is enough for normal exception handling.

Is it only me seeing a self-contradiction in 'normal exception'?
But here are some ideas for renaming:

resumeable exception -> retro-fitted pre-condition
or: negotiated pre-condition

or sumply resumption?

My mental picture is that of an outer scope in the call chain
attempting to meet the requirements communicated from an unknown
inner scope and then resuming where the failed pre-condition
was spotted. But I wouldn't require this to be implemented in
a very efficient way! When you need event handling, implement
it. E.g. the fail form needs to preserve the call chain
until the failure is handled. Handling might be as easy as
evaluation in void context or usage of // in a non-fatal scope.
Otherwise a CATCH block is needed. Or perhaps a RESUME block
and a correspondingly enhanced require form? That would integrate
the AUTODEF and friends with the error/exception/control/assertion
system. To illustrate the idea:

if !$file.writeable { require $file.writeable; }

or perhaps even without the if. And then somewhere up the call chain

RESUME
{
when .writeable { make $! writeable; resume }

# more typeish syntax
when File::writeable { make $! writeable; resume }
}
# with type outside
RESUME File::writeable
{
make $! writeable; resume
}

The resume could actually be the default way to exit a RESUME block.


> As for recovery - the way it's done can be specialized on top of
> continuations, but a continuation for the code that would run had
> the exception not been raised is the bare metal support we need to
> do this.

Yep, we need to agree on a form how to communicate unmet requirements
outwards. The only entity that comes to my mind that usually matches
requirements is the type system or meta information repository.


> Where this gets taken further by (IMHO overly abstract) exception
> handling libraries and standardization is a question that
> application development kits (e.g. Catalyst, GTK, Cocoa) must
> develop a policy for.

Well, yes! A non-trivial framework will install a bunch of types.
A subset of which beeing exception (sub)types. The challenge is then
shifted to inter-framework unification =8)
But one thing that should work is tunneling standard resumption requests
through such a framework.

Austin Hastings

unread,
Oct 7, 2005, 10:28:13 AM10/7/05
to Yuval Kogman, perl6-l...@perl.org
Yuval Kogman wrote:

>On Fri, Oct 07, 2005 at 02:31:12 -0400, Austin Hastings wrote:
>
>
>>Yuval Kogman wrote:
>>
>>
>>
>>>Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this.
>>>
>>>Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option.
>>>
>>>
>>>
>>>
>>I think that what your proposal *really* requires is uniformity. There are other ways to get the same behavior, including an abstract factory interface for exception construction (would provide "virtual constructor" for exceptions, so permitting userspace to insert a 'retry'
>>behavior), but it has the same vulnerability: the p6core must cooperate in uniformly using the same mechanism to report errors: throw, fail, die, error, abend, whatever it's eventually called.
>>
>>
>
>We have:
>
> die: throw immediately
>
> fail: return an unthrown exception, which will be thrown depending on whether our caller, and their caller - every scope into which this value propagates - is using fatal.
>
>This is enough for normal exception handling.
>
>

Yet here we are discussiong abnormal exception handling.

>As for recovery - the way it's done can be specialized on top of
>continuations, but a continuation for the code that would run had
>the exception not been raised is the bare metal support we need to
>do this.
>
>

No, it isn't. It's *one way* to do this. Any mechanism which transfers
control to your error-recovery code in such a way that can cause an
uplevel return of a substituted value is the "bare metal support we
need". You're conflating requirements and design.

I suggested an alternative *design* in my prior message, to no avail.
Try this instead:

You overload the global 'die' with a sub that tries to decode the error
based on its arguments. If it cannot comprehend the error, it invokes
P6CORE::die(). If it comprehends the error, it tries to resolve it
(querying the user, rebooting the machine to free up space in /tmp,
whatever) and if successful it returns the fixed value.

But wait! This requires that everyone do "return die ..." instead of
die..., and we can't have that. So you add a source filter, or macro, or
tweak the AST, or perform a hot poultry injection at the bytecode level,
or whatever is required to convert "die" into "return die" whereever it
occurs.

Et voila! No exceptions are caught, no continuations are released into
the wild. And yet it flies. Much like the hummingbird it looks a little
awkward, and I'm way not sure that munging bytecodes is necessarily a
better idea. But the point is that "resuming from an exception" (or
appearing to) is not bound to "implemented with continuations".

=Austin

"Et vidi quod aperuisset Autrijus unum de septem sigillis, et audivi
unum de quatuor animalibus, dicens tamquam vocem tonitrui : Veni, et
vide. Et vidi : et ecce camelus dromedarius, et qui scriptat super
illum, habebat archivum sextum, et data est ei corona, et exivit haccum
ut vinceret."
Apocalypsis 6:1 (Vulgata O'Reilly)

Yuval Kogman

unread,
Oct 7, 2005, 11:27:28 PM10/7/05
to Austin Hastings, perl6-l...@perl.org
On Fri, Oct 07, 2005 at 10:28:13 -0400, Austin Hastings wrote:

> But the point is that "resuming from an exception" (or
> appearing to) is not bound to "implemented with continuations".

What's the point?

Continuations are good for exactly this purpose. Parrot already
supports them. I see absolutely no reason why we would want to
implement this any other way but using continuations.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: *shu*rik*en*sh*u*rik*en*s*hur*i*ke*n*: neeyah!!!!

0 new messages