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

use fatal err fail

2 views
Skip to first unread message

Adam D. Lopresto

unread,
Sep 28, 2005, 12:46:37 PM9/28/05
to perl6-l...@perl.org
The recent thread on Expectuations brought back to mind something I've been
thinking for a while. In short, I propose that "use fatal" be on by default, and
that "err" be turned into syntactic sugar for a very small try/CATCH block.

Basically, my observation is that no-one consistently checks the return values of
the built-in functions. As Mama[1] can attest, lots and lots of code is posted to
Perl Monks calling open without checking the return value. Even among those who
check the return value of open, just about none check for close[2]. And have you
*ever* seen a program that actually checks the return value of print[3]?

Exception handling seems to provide a good solution to this. Instead of writing
"open ... or die ..." every time, we can just blindly assume the open worked, and
catch the error later. If we don't catch it, it does exactly what the "die" would
have anyway, so we're no worse off. The exception propagates upwards until it's
handled, or it terminates the program. At least the program doesn't continue
blindly, believing all to be well. The problem with typical exception handling
code is that it quickly becomes bulky and ugly. Compare.

#ignore any files we don't have access to
for @files {
my $fh = open $_ or next;
load_config_from($fh);
}

with

#ignore any files we don't have access to
for @files {
my $fh;
try {
$fh = open $_;

CATCH {
next;
}
}
load_config_from($fh);
}

So returning a special value makes checking the return code simpler, but hurts us
if we forget. Throwing an exception saves us from forgetting, but has the
nastiness of catching that exception.

I propose that the spelled out version of // be renamed to something like "dor".
(I'm willing to accept other suggestions on that.) "err" becomes a
single-expression level try/CATCH block. The left hand side is executed, and if it
successfully returns anything (even undef), it's value is used. If it throws an
exception, however, that exception is caught and the right hand side is evaluated
and used. So that code becomes

for @files {
my $fh = open $_ err next;
load_config_from($fh);
}

which is just syntactic sugar for

for @files {
my $fh = try {
open $_;

CATCH {
next;
}
}
}

"err" would bind tighter than assignment, so it can be used to provide a fall-back
value to use in case the normal flow of control fails. Using "no fatal" is still
allowed, but you can achieve almost the same thing [4] by adding "err $! but undef"
after calls whose failure you want to handle that way.

I think on the whole this gives us a way to allow exceptions to be used everywhere,
while making it clean and simple enough that it doesn't bog us down too much.

[1] http://www.perl.com/pub/a/2005/09/22/onion.html?page=6
[2] I've actually seen data lost due to this. When drive space is very limited
(due to, for instance, a user quota) it's often possible to open a new file (since
there's some space left), but the close fails since too much was written to it.
[3] Actually, I'm not sure this is fair. It seems that, due to buffering and other
things, print returns true even when it doesn't actually succeed. But why let
facts get in the way of rhetoric?
[4] The difference is that "no fatal" would only affect code that calls "fail"
itself. The "err" would affect code that directly calls "die", too.
--
Adam Lopresto
http://cec.wustl.edu/~adam/

Eschew obfuscation!

Yuval Kogman

unread,
Sep 28, 2005, 8:24:27 PM9/28/05
to Adam D. Lopresto, perl6-l...@perl.org
On Wed, Sep 28, 2005 at 11:46:37 -0500, Adam D. Lopresto wrote:
> The recent thread on Expectuations brought back to mind something I've been
> thinking for a while. In short, I propose that "use fatal" be on by default, and
> that "err" be turned into syntactic sugar for a very small try/CATCH block.

I like it a lot. It gives the advantages of both the flexible, more
robust try/catch, and the (locally) concise, clear error return.

The only problem I see is that most people (i don't know why) argue
that 'use fatal' should be off by default.

Your proposal still makes a lot of sense even if 'use fatal' is
optional, though.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &
/\ kung foo master: /me climbs a brick wall with his fingers: neeyah!

Darren Duncan

unread,
Sep 29, 2005, 5:32:10 AM9/29/05
to perl6-l...@perl.org
At 11:46 AM -0500 9/28/05, Adam D. Lopresto wrote:
>The recent thread on Expectuations brought back to mind something I've been
>thinking for a while. In short, I propose that "use fatal" be on by default,

I totally, TOTALLY, agree with this.

EVERY built-in function and operator that talks to program-external
resources should by default throw an exception on failure, including
all system calls, file or filesystem operations, network operations,
interprocess communications, signals, etc.

It is a lot easier to write safe, correctly functioning, and easy to
debug programs when problems are detected as early as possible, which
a default-active exception architecture ensures.

Naive developers will be helped greatly when they forget to account
for possible problems, since errors don't crop up in a different
place than their cause (eg, at 'print' time when the problem was with
the 'open'); if they forget something, Perl will tell them.

Saavy developers who don't like the exceptions can explicitly turn
them off, the very act of which implies that they know what they're
doing and can handle the situations where Perl doesn't help them
remember.

Considering that its a well known best practice to throw exceptions
in one's own non-trivial programs and modules, it would be nice if
the core language functions themselves were consistent with that
practice.

Just as I agree that having 'use strict' turned on all the time
and/or being the default, likewise having 'use fatal' turned on all
the time and/or by default is also the best course of action. While
we're at it, 'use warnings' should probably be on by default too, but
this is less important than 'fatal'.

-- Darren Duncan

TSa

unread,
Sep 29, 2005, 6:53:51 AM9/29/05
to perl6-l...@perl.org
HaloO,

Yuval Kogman wrote:
> On Wed, Sep 28, 2005 at 11:46:37 -0500, Adam D. Lopresto wrote:
>
>>The recent thread on Expectuations brought back to mind something I've been
>>thinking for a while. In short, I propose that "use fatal" be on by default, and
>>that "err" be turned into syntactic sugar for a very small try/CATCH block.
>
>
> I like it a lot. It gives the advantages of both the flexible, more
> robust try/catch, and the (locally) concise, clear error return.

I don't like it at all. I fear, that we mix two orthogonal concepts
just because it is convenient.

To me the statement

return 42; # 1

has to orthogonal meanings:

1) the current scope has reached its (happy) end
2) a specific result was determined

We can vary on both of these dimensions *independently*!
Which gives the remaining three cases:

return undef; # 0 unspecific result
fail undef; # -1 no return with unspecific reason
fail 42; # -2 no return but determined reason

In other words an exception means

return !caller;

or in yet another way to describe my attitude: the least
thing that *defines* an exception is that the dynamic
scope in question has reached the conclusion that it
is *not* going to give control back to its creator!

Note that this somewhat pinpoints or controls the two infinities
of computing in time and storage. One either needs a block of infinite
length right away or handle control flow exceptions *on the outside*
to keep a finite scope going.
--
$TSa.greeting := "HaloO"; # mind the echo!

Yuval Kogman

unread,
Sep 29, 2005, 7:08:02 AM9/29/05
to TSa, perl6-l...@perl.org
On Thu, Sep 29, 2005 at 12:53:51 +0200, TSa wrote:
> I don't like it at all. I fear, that we mix two orthogonal concepts
> just because it is convenient.

"just because it is convenient" is precicely why I like it =)

> To me the statement
>
> return 42; # 1
>
> has to orthogonal meanings:
>
> 1) the current scope has reached its (happy) end
> 2) a specific result was determined

return literally means "give back". The caller is getting a value
that we give. This implies two completely non-orthogonal meanings to
me:

1) the scope has reached its happy end
2) it's reached the end because a specific result was determined

> We can vary on both of these dimensions *independently*!
> Which gives the remaining three cases:
>
> return undef; # 0 unspecific result

just 'return' would do. But This is odd, unless the context is void
- if the caller is expecting something, wer're not returning an
"unspecific result" - we're returning a value. This value is in
bounds (it's a normal perl scalar) and it has a well known meaning
that "i have have nothing meaning to report".

I think this is actually a very concrete result.

> fail undef; # -1 no return with unspecific reason
> fail 42; # -2 no return but determined reason

Fail is, semantically:

(should_die ?? die !! return)

and the caller gets to determine the behavior.

Whether failures have an extras payload in a simple return
value, or whether the payload is carried via an out of bounds route
to the closest thing that can handle is really only dependant on how
the caller is most comfortable with handling errors.

> or in yet another way to describe my attitude: the least
> thing that *defines* an exception is that the dynamic
> scope in question has reached the conclusion that it
> is *not* going to give control back to its creator!

Not at all!

It's simply returning an out of bounds value to the closest caller
that is willing to handle out of bounds values.

In fact, it's short circuiting to it's caller, to give it control as
fast as possible since it doesn't know what to do anymore.

The reason exceptions tend to pop upwards more is that they are
handled in a way that if the immediate caller doesn't know how to
handle it either, it keeps going up the call stack till a calling
scope does know what to do, and can explcititly take control.

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

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

Ingo Blechschmidt

unread,
Sep 29, 2005, 8:34:54 AM9/29/05
to perl6-l...@perl.org
Hi,

TSa wrote:
> Yuval Kogman wrote:
>> On Wed, Sep 28, 2005 at 11:46:37 -0500, Adam D. Lopresto wrote:
>>>thinking for a while. In short, I propose that "use fatal" be on by
>>>default, and that "err" be turned into syntactic sugar for a very
>>>small try/CATCH block.
>>
>> I like it a lot. It gives the advantages of both the flexible, more
>> robust try/catch, and the (locally) concise, clear error return.
>
> I don't like it at all. I fear, that we mix two orthogonal concepts
> just because it is convenient.

I agree with you, TSa. I'd like to add that we don't have to do without
convenience in the majority of cases:

use fatal;

for @files {
my $fh = try { open $_ } err next;

load_config_from($fh);
}

* try {...} is, like Perl 5's eval {...}, *not* statement-level.
This means you can easily wrap it around an expression.
Particularly you can write
my $fh = try { open $_ } err next; # instead of

my $fh;
try {
$fh = open $_;
CATCH { next }
};

* Yes, "try {...} err ..." does not check the exception type, but
the proposed "... err ..." doesn't either.

* "try { foo() } err next" will next even if foo() did not throw
an exception, but returned undef. But I don't think that's a problem
in most cases. One can always do:
try { foo(); 1 }


FWIW, I also agree with Darren that "use fatal" should be on by default.


--Ingo

Carl Franks

unread,
Sep 29, 2005, 9:05:05 AM9/29/05
to Ingo Blechschmidt, perl6-l...@perl.org
On 29/09/05, Ingo Blechschmidt <ibl...@web.de> wrote:

> * "try { foo() } err next" will next even if foo() did not throw
> an exception, but returned undef. But I don't think that's a problem
> in most cases. One can always do:
> try { foo(); 1 }

I think that's a flag that it's not a great idea.

For the example given, I'd rather write
my $fh = try {open $_; CATCH {next} };

and still fit it on 1 line!

For what it's worth, I'd like to chime in on 'yes please' for having
fatals on by default.
I've been hanging round some perl forums lately, and it's such a
basic, recurrent issue.

Cheers,
Carl

Austin Hastings

unread,
Sep 29, 2005, 1:52:54 PM9/29/05
to TSa, perl6-l...@perl.org
TSa wrote:

> HaloO,
>
> Yuval Kogman wrote:
>
>> On Wed, Sep 28, 2005 at 11:46:37 -0500, Adam D. Lopresto wrote:
>>
>>> The recent thread on Expectuations brought back to mind something
>>> I've been
>>> thinking for a while. In short, I propose that "use fatal" be on by
>>> default, and
>>> that "err" be turned into syntactic sugar for a very small try/CATCH
>>> block.
>>
>>

You already know that "err" is the low-precedence version of //, right?
What replaces that? I like "default" or "defaults" myself, but I'm never
really sure what the precedence actually IS. After all, "and"/"or" were
lower than assignment, so you could code:

$a = foo or die;

and get ($a or die). How does this work for the "err"/"defaults"
keyword? Does the low-precedence version move up, or is there an idiom I
don't understand?

On the primary hand, I don't like the idea of using "err" as a try/catch
because that's putting exception handling in line with the primary code.
See FMTYEWT below.

>>
>> I like it a lot. It gives the advantages of both the flexible, more
>> robust try/catch, and the (locally) concise, clear error return.
>
>
> I don't like it at all. I fear, that we mix two orthogonal concepts
> just because it is convenient.
>
> To me the statement
>
> return 42; # 1
>
> has to orthogonal meanings:
>
> 1) the current scope has reached its (happy) end
> 2) a specific result was determined
>
> We can vary on both of these dimensions *independently*!
> Which gives the remaining three cases:
>
> return undef; # 0 unspecific result
> fail undef; # -1 no return with unspecific reason
> fail 42; # -2 no return but determined reason
>
> In other words an exception means
>
> return !caller;
>
> or in yet another way to describe my attitude: the least
> thing that *defines* an exception is that the dynamic
> scope in question has reached the conclusion that it
> is *not* going to give control back to its creator!


But it *does* give control, albeit briefly, back to its caller.

A million years ago, $Larry pointed out that when we were able to use
'is just a' classifications on P6 concepts, it indicated that we were
making good forward progress. In that vein, let me propose that:

* Exception handling, and the whole try/catch thing, IS JUST An awkward
implementation of (late! binding) run-time return-type MMD.

Specifically, if I promise you:

sub foo() will return Dog;

and later on I actually wind up giving you:

sub foo() will return Exception::Math::DivisionByZero;

the try/catch paradigm essentially says:

I wanted to call <c>sub Dog foo()</c> but there may be times when I
discover, after making the call, that I really needed to call an anonymous
<c>sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }</c>.

We're conditionally editing the return stack. This fits right in with
the earlier thread about conditionally removing code from the inside of
loops, IMO. Once you open this can, you might as well eat more than one
worm. Another conceptually similar notion is that of AUTOLOAD. As a perl
coder, I don't EVER want to write

say "Hello, world"
or die "Write to stdout failed.";

-- it's "correct". It's "safe coding". And it's stupid for a whole bunch
of reasons, mostly involving the word "yucky". But I acknowledge that
through the miracle of broken pipes, it can legitimately happen that
stdout will fail while stderr is a viable diagnostic mechanism.

Instead, I want PERL to fill that in for me: I believe that the default
error mechanism should debug my program, the shell script that calls my
program, and the actions (including blood alcohol content) of the user
of my program over the last 24 hours: lets leave <c>use autodebug;</c>
turned on by default.

The 'Exceptuation' proposal seems to me to include two things:

1. A 'RESUME' feature.
2. An implicit acknowledgement that the default implementations are
parallel:

{...
CATCH -> $e {throw $e;} # Going up?
RESUME -> $r {resume $r;} # Going down?
}

The rest is optimization. If caller() includes an array of
continuations, then <c>throw</c> looks like a loop up the array:

sub throw(Exception $e)
{
reverse caller() ==> { .continuation($! => $e) if does(CATCH); }
}

But the default behavior (modulo threads) is going to unlink all the
stack frame pages when the continuation is invoked. So there has to be
yet another copy of the links to the stack, because the exception
handling will want to call functions and build who-knows-what elaborate
skycastles. And it must be reentrant because of the possibility of
exceptions during the exception handling. Which means that the call
stack needs to be stored in the Exception. [The list of things in the
exception gets pretty long. I'm sure it's all a ref to the last page of
the call stack, so it doesn't gobble up much space, but there's a lot of
"and you'll want"s coming up.]

So <c>resume</c> is a multi, no? (Or it could just be a method:
$!.resume, but that doesn't read as well in a block that *really* should
be as readable as possible.) Also, any layer of exception handling may
do some nontrivial amount of work before encountering its own exception:

{
...
CATCH -> $e
{
do_some_work; # exception in here?
do_more_work; # Or in here?
resume $e;
}
}

Which kind of argues that either (A) very fine control of RESUME blocks
must be available:

{...
CATCH -> $e
{
do_some_work;
RESUME {...}
do_more_work;
RESUME {...}
}
}

or that (B) very fine identification of exceptions be available,
*including from the p6 library*. Perhaps typed resumptions are as
important as typed exceptions?

sub copy_file($from, $to)
{
...
RESUME -> $r
{
when Resumption::Microsoft::AbortRetryFail::Abort { last; }
when Resumption::Microsoft::AbortRetryFail::Retry { continue; }
when Resumption::Microsoft::AbortRetryFail::Fail { return FAILED; }
}
}

I don't actually like this, because I don't like the whole notion of
upper-case blocks: it's stuff that the really good perl IDE is going to
hide so that the "real" code shows through.

Also, of course, there's the question of leverage: throwing an exception
is a way of quickly saying "something happened outside the expected".
One enormous benefit of $perl[1..5] was that the exceptions were
huffmanized really highly: <c>die</c>. This was good not because
exceptions were common, but because they were uncommon, and allocating a
short token allowed the reader to bypass them quickly. The whole "or die
message" idiom worked for that reason:

if (print("Hello, world!") == -1)
{
print STDERR "Could not write greeting message";
exit 1;
}

vs.

print "Hello, world"
or die "Could not write greeting message";

I think that moving the <c>die</c> into <c>print</c> is a win, but it
disconnects the diagnostic a little bit:

print "Hello, "
or die "Could not write first part";
print "world!\n"
or die "Could not write second part";

Becomes a single generic failure message thrown by:

print "Hello, ";
print "world!\n";

Abstractly, you want the generic-ness, but simultaneously want to think
that someone can catch these errors at the relevant place and quickly
tailor them. The right place, of course, is the sub or method that is
doing the work. I want to be able to CATCH local exceptions locally,
too! Not because "I don't know what to do", but in a more Knuth/literate
sense, I don't want to write about what to do in-line with my beautiful
algorithm:

sub greet
{
print "Hello, " # or throw PrintException;
print "world\n" # or throw PrintException;

CATCH
{
when PrintException {...}
}
}

But I want to know which PART of my elaborate greeting failed. In the p5
version I could discriminate between first-part failure and second-part
failures. Why has p6 backslid into mealy-mouthed genericity?

One way, Yuval's suggestion, is to make "err" be a quicky catch mechanism:

sub greet
{
print "Hello, " err die "Could not write first part";
print "world!\n" err die "Could not write second part";
}

But that's not abstracting anything. It just keeps the same, tedious,
check-every-result coding layout and gives it fractionally worse
Huffmanization. What I want is to catch my errors elsewhere, and still
be able to localize their solution. Cleverly, there's already a way to
do this: labels. All I need is to know where I'm at, label-wise, and
I've got a dandy way to mark my code up.

sub greet
{
FirstPart:
print "Hello, ";

SecondPart:
print "world!\n" ;

CATCH
{
when PrintException
{
fprintf(STDERR, "Could not write %s part",
caller.label.tolower.s/part$//);
}
}
}

This is a little more important than it seems, because for resumptions
it will likely be important to know what work has been done. Likewise,
any and all kinds of error handlers will want to know about resources
that are allocated but need to be put back. The <c>finally</c> block is
the ultimate admission of failure in this regard:

[quote=http://java.sun.com/docs/books/tutorial/essential/exceptions/finally.html]
The final step in setting up an exception handler is to clean up before
allowing control to be passed to a different part of the program. You do
this by enclosing the clean up code within a |finally| block. The
|finally| block is optional and provides a mechanism to clean up
regardless of what happens within the |try| block. Use the |finally|
block to close files or to release other system resources.
[/quote]

But resumptions are going to let us FIX the problem that occurred when
we tried to open the 256th filehandle. So all the directory parsing,
file exploring, and whatnot is going to be *preserved*. Thus we don't
actually want to automatically finalize all that stuff unless we know
for sure that we aren't going to resume: even *later* binding. If there
were a FINALLY clause (let us pray for deliverance, here, banjos and
all) it couldn't be run until a CATCH block exited.

In the meantime, we're trying to resume from exception handling. So like
the gynecologist who changed jobs to become a motorcycle mechanic, we
want to do all our work by reaching through the tailpipe. The exception
[here we go again] needs to bundle along a stack that provides access to
the variable scope. Yet another use for a Pascal's <c>with</c> keyword:

# NB: Testing new MySql 5 server
BEGIN
{
CATCH -> $e
{
when Exception::Database::Mysql::Connect
{
with $e.namespace
{
if $dsn ~~ 'mysql:'
{
$dsn.s/^mysql:/mysql5:/;
$e.resume;
}
}
}
}
}

=Austin

Yuval Kogman

unread,
Sep 29, 2005, 5:27:02 PM9/29/05
to Austin Hastings, TSa, perl6-l...@perl.org
On Thu, Sep 29, 2005 at 13:52:54 -0400, Austin Hastings wrote:

> You already know that "err" is the low-precedence version of //, right?
> What replaces that? I like "default" or "defaults" myself,

Yes, he proposed 'dor'.

As I see it err is like this:

sub infix:<err> ($lhs is delayed, $rhs is delayed) { # we really need thunk parameters
return $lhs dor $rhs;
CATCH { return $rhs }
}

So really 'err' is just 'dor' with an added error silencer.



> On the primary hand, I don't like the idea of using "err" as a try/catch
> because that's putting exception handling in line with the primary code.
> See FMTYEWT below.

Isn't

no fatal;

my $fh = open $file or die;
...
my $fh = open $file or next;

also putting exception handling in line with the primary code?

Isn't that useful?

Exception handling should be put in wherever there is well defined
behavior on how error conditions should be dealt with.

It's Perl's job to make this as easy as possible for the programmer.

If we have a module called File::Slurp::Some, which doesn't really
care if some of the files don't exist, it's code would, IMHO be
much more readable with

open $file err next;

than a catch block.

The reason for this is that a catch block is always detached from
the code.

However, if the error is part of the normal flow (it is no longer
exceptional for the file to not exist), what benefit does the added
detachment give?

> A million years ago, $Larry pointed out that when we were able to use
> 'is just a' classifications on P6 concepts, it indicated that we were
> making good forward progress. In that vein, let me propose that:
>
> * Exception handling, and the whole try/catch thing, IS JUST An awkward
> implementation of (late! binding) run-time return-type MMD.

Exception handling is just continuation passing style with sugar.

Have a look at haskell's either monad. It has two familiar keywords
- return and fail.

Every statement in a monadic action in haskell is sequenced by using
the monadic bind operator.

The implementation of >>=, the monadic bind operator, on the Either
type is one that first check to see if the left statement has
failed. If it does, it returns it. If it doesn't it returns the
evaluation of the right hand statement.

Essentially this is the same thing, just formalized into a type....

> Specifically, if I promise you:
>
> sub foo() will return Dog;
>
> and later on I actually wind up giving you:
>
> sub foo() will return Exception::Math::DivisionByZero;

In haskell:

foo :: Either Dog Exception::Math::DivisionByZero

e.g., it can return either the expected type, or the parameter.

Haskell is elegant in that it compromises nothing for soundness, to
respect referential integrity and purity, but it still makes thing
convenient for the programmer using things such as monads


> the try/catch paradigm essentially says:
>
> I wanted to call <c>sub Dog foo()</c> but there may be times when I
> discover, after making the call, that I really needed to call an anonymous
> <c>sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }</c>.

Yes and no.

The try/catch mechanism is not like the haskell way, since it is
purposefully ad-hoc. It serves to fix a case by case basis of out
of bounds values. Haskell forbids out of bound values, but in most
programming languages we have them to make things simpler for the
maintenance programmer.

> We're conditionally editing the return stack. This fits right in with
> the earlier thread about conditionally removing code from the inside of
> loops, IMO. Once you open this can, you might as well eat more than one
> worm. Another conceptually similar notion is that of AUTOLOAD. As a perl
> coder, I don't EVER want to write
>
> say "Hello, world"
> or die "Write to stdout failed.";
>
> -- it's "correct". It's "safe coding". And it's stupid for a
> whole bunch of reasons, mostly involving the word "yucky".

It's incorrect because it's distracting and tedious.

http://c2.com/cgi/wiki?IntentionNotAlgorithm

Code which does it is, IMHO bad code because obviously the author
does not know where to draw the line and say this is good enough,
anything more would only make it worse.

> But I acknowledge that through the miracle of broken pipes, it can
> legitimately happen that stdout will fail while stderr is a viable
> diagnostic mechanism.

the default $SIG{PIPE} handler is a wonderful example of how nice
exception handling is for naive code.

$SIG{PIPE}'s only problem is that it's completely non standard.

> But the default behavior (modulo threads) is going to unlink all the
> stack frame pages when the continuation is invoked.

Forget the stack... Perl is CPS under the hood, even if it's
optimized not to be that way.

> So there has to be yet another copy of the links to the stack,
> because the exception handling will want to call functions and
> build who-knows-what elaborate skycastles. And it must be
> reentrant because of the possibility of exceptions during the
> exception handling.

Reentrancy is an implementation detail best left unmentioned.

Assume that every bit of code you can run in perl 6 is first class
code - it gets safety, calls, control flow, exceptions, and so
forth.

Yes, even signals and exceptions.

The runtime is responsible for making these as fast as possible
without being unsafe.

> Which means that the call stack needs to be
> stored in the Exception.

The .resume continuation in the exception is equivalent to the call
stack, as is any other continuation, implicit or explicit.

> [The list of things in the exception gets pretty long. I'm sure
> it's all a ref to the last page of the call stack, so it doesn't
> gobble up much space, but there's a lot of "and you'll want"s
> coming up.]

Yadda yadda, static analysis could solve this.

A one liner with no catch blocks whatsoever could be implemented
without CPS at all, and this can be determined at compile time
pretty easily.

> So <c>resume</c> is a multi, no? (Or it could just be a method:
> $!.resume, but that doesn't read as well in a block that *really* should
> be as readable as possible.)

It can't be a method because it never returns to it's caller - it's
a continuation because it picks up where the exception was thrown,
and returns not to the code which continued it, but to the code that
would have been returned to if there was no exception.

It is, IMHO a multi though. There is no reason that every
continuation cannot be a multi, because a continuation is just a
sub.

I don't know if there are method continuations - i guess there could
be, but there's no need to permutate all the options when the
options can compose just as well.

> Also, any layer of exception handling may do some nontrivial
> amount of work before encountering its own exception:

That's OK.

> Which kind of argues that either (A) very fine control of RESUME blocks
> must be available:

just assume they work inside out when being resolved. This is just
recursion into an alternative control flow, which is then stepped
out of if there were as many .resumes as exceptions were thrown.
There is no fundamental difference between exceptions and normal
code once you accept continuations.

>
> {...
> CATCH -> $e
> {
> do_some_work;
> RESUME {...}

I don't understand this at all.

What does it mean? what exception is it?

I think maybe this is what you wanted:

{
error_throwing_code;
CATCH {
do_some_work;
CATCH {
# the exception raised in the exception handler is
# caught here
}
}
}

> sub copy_file($from, $to)
> {
> ...
> RESUME -> $r
> {
> when Resumption::Microsoft::AbortRetryFail::Abort { last; }
> when Resumption::Microsoft::AbortRetryFail::Retry { continue; }
> when Resumption::Microsoft::AbortRetryFail::Fail { return FAILED; }
> }
> }
>
> I don't actually like this, because I don't like the whole notion of
> upper-case blocks: it's stuff that the really good perl IDE is going to
> hide so that the "real" code shows through.

Exceptions are real code too - especially ones that are being dealt
with.

It's just slightly more aspect oriented in style.

In fact, the whole point of AOP is to remove what they call cross
cutting concerns (like logging, persistence, thread safety, etc)
from the main code to keep it clean, without making the important
support code invisible. They do this by adding what they call
aspects from a third person perspective, into the control flow of
the normal code, by specifying where the aspect gets control in a
declarative style.

> Also, of course, there's the question of leverage: throwing an exception
> is a way of quickly saying "something happened outside the expected".

That's all it is.

> that someone can catch these errors at the relevant place and quickly
> tailor them. The right place, of course, is the sub or method that is
> doing the work. I want to be able to CATCH local exceptions locally,
> too! Not because "I don't know what to do", but in a more Knuth/literate
> sense, I don't want to write about what to do in-line with my beautiful
> algorithm:
>
> sub greet
> {
> print "Hello, " # or throw PrintException;
> print "world\n" # or throw PrintException;
>
> CATCH
> {
> when PrintException {...}
> }
> }
>
> But I want to know which PART of my elaborate greeting failed. In the p5
> version I could discriminate between first-part failure and second-part
> failures. Why has p6 backslid into mealy-mouthed genericity?

It hasn't.. We still have 'or die' for raising exceptions, and you
can use, like you mentioned

> One way, Yuval's suggestion, is to make "err" be a quicky catch mechanism:

It's not my suggestion, btw ;-)

> sub greet
> {
> print "Hello, " err die "Could not write first part";
> print "world!\n" err die "Could not write second part";
> }
>
> But that's not abstracting anything. It just keeps the same, tedious,
> check-every-result coding layout and gives it fractionally worse
> Huffmanization.

Well, there is a conflict here:

you want to mark and not mark your errors at the same time.

> What I want is to catch my errors elsewhere, and still
> be able to localize their solution. Cleverly, there's already a way to
> do this: labels. All I need is to know where I'm at, label-wise, and
> I've got a dandy way to mark my code up.

This is feasable... I don't see why it shouldn't be implemented, but
frankly I don't see myself using it either.

This is because the types of exceptions I would want to resume are
ones that have a distinct cause that can be mined from the exception
object, and which my code can unambiguously fix without breaking the
encapsulation of the code that raised the exception.

Take OSX for example - it's very consistent in that you are allowed
to try to perform every action you like. Whenever there's a
permission denied error, it pops up a password dialog.

I think that a nice user oriented environment can benefit from a
library of generic exception softners that interact with the user:

change the mode of ureadable/writable files

enter a password for elevated priviliges

decide whether to keep trying when something seems to be timing
out

etc etc.

These are all very specific and reusable, regardless of what we're
resuming. This is nice because especially if we're trying to e.g.

my @handles = map { open $_ } @file_names;

we can let the user skip unreadable files, by using return with no
value instead of returning a handle. This is a generic pattern that
enhances the user experience by not forcing the user to start over
again.

For more specific code, i doubt there will be several exceptions of
the same kind in a call chain.


> CATCH
> {
> when PrintException
> {
> fprintf(STDERR, "Could not write %s part",
> caller.label.tolower.s/part$//);

The catch block's caller is greet's caller.

I guess you want

$!.position.label.yadda

or something like that

> [quote=http://java.sun.com/docs/books/tutorial/essential/exceptions/finally.html]
> The final step in setting up an exception handler is to clean up before
> allowing control to be passed to a different part of the program.

For cleanup we have real useful garbage collection, with a
declarative interface to decide on how an object needs to disappear.

Resource management is the task of the resource creator, not
exception handler, IMHO. It's just too hard to get it right.

So your point:

> But resumptions are going to let us FIX the problem that occurred when
> we tried to open the 256th filehandle. So all the directory parsing,
> file exploring, and whatnot is going to be *preserved*.

is very valid

> Thus we don't
> actually want to automatically finalize all that stuff unless we know
> for sure that we aren't going to resume: even *later* binding.

Continuations give this behavior consistently.

They just need to be garbage collected themselves, in a way.

What I would like to disallow is the ability to take $! and just
.resume it out of the blue in code that isn't an exception handler.
That just does't make sense:

try { ... }
some_other_stuff;
$!.resume if $!;

> [here we go again] needs to bundle along a stack that provides access to
> the variable scope.

Again, that is exactly what a continuation is, and parrot already
has them =)


> Yet another use for a Pascal's <c>with</c> keyword:

I actually quite liked that RFC =)

The problem is that records in a static language are static, and
hashes are dynamic.

There is no way to tell the user at compile time that a variable
doesn't exist because we don't know if there's such a hash key.

This means that 'use strict' cannot work in a 'with'ed block.

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

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

TSa

unread,
Sep 30, 2005, 11:09:23 AM9/30/05
to perl6-l...@perl.org
HaloO,

Yuval Kogman wrote:
> The try/catch mechanism is not like the haskell way, since it is
> purposefully ad-hoc. It serves to fix a case by case basis of out
> of bounds values. Haskell forbids out of bound values, but in most
> programming languages we have them to make things simpler for the
> maintenance programmer.

My view here is that the parameters in the code pick a point in
the range from free on the one end and bound at the other end.
The unifying concept is constraints. So I see the following boundary
equalities:

unconstraint = free # 0.0
contstraint # 0.0^..^1.0
fully constraint

as the black and white ends with shades of gray in the middle.
And it is the type system that guaranties the availability
of the required information e.g. in $!. In that sense a sub
with a CATCH block is a different type than one without. This
difference is taking into account when dispatching exceptions.


> Reentrancy is an implementation detail best left unmentioned.

Uh ohh, in an imperative language with shared state outside
the unshared state of multiple invocations of one sub the reentrance
proplem is just there. Interestingly it is easily unifyable with
shared data.


> Assume that every bit of code you can run in perl 6 is first class
> code - it gets safety, calls, control flow, exceptions, and so
> forth.

Just to synchronize our understanding, I see the following
equivalences from the data and code domains

data code

class = sub
instance = invocation

To illustrate my view consider

class foo
{
has ...
method ...
}

and match with

sub foo
{
has ... # my named parameters defined in body proposal

BEGIN ...
CATCH ...

label:
}

What I want to say is that calling a sub means creating an
instance of the class that describes---or *constrains*---
the potential invocations. If such an invocation is left
lying in memory unfinished we have a continuation. How concurrent
these continuations are stepped in real time with respect to their
respective inner causal logic is irrelevant to the concept.
But *causality* is important!

The view I believe Yuval is harboring is the one examplified
in movies like The Matrix or The 13th Floor and that underlies
the holodeck of the Enterprise: you can leave the intrinsic
causality of the running program and inspect it. Usually that
is called debugging. But this implies the programmer catches
a breakpoint exception or some such ;)

Exception handling is the programmatic automatisation of this
process. As such it works the better the closer it is in time
and context to the cause and the more information is preserved.
But we all know that a usefull program is lossy in that respect.
It re-uses finite resources during its execution. In an extreme
setting one could run a program *backwards* if all relevant
events were recorded!


> Yes, even signals and exceptions.
>
> The runtime is responsible for making these as fast as possible
> without being unsafe.

Hmm, I would see the type system in that role. It has all the
information of the interested parties in a longjump. If it knows
there are no potential handlers

> It can't be a method because it never returns to it's caller - it's

It beeing the CATCH block? Then I think it *is* in a certain
way a method with $! as it's invocant. HiHi and here a locality
metric for dispatch applies. BTW, how is the signature of a CATCH
block given? Simply

CATCH SomeException {...}

or is inspection with cascaded when mandatory?


> a continuation because it picks up where the exception was thrown,

I would say it is given information about. In a way an exception
handler is dispatched on the type of exception.

> and returns not to the code which continued it, but to the code that
> would have been returned to if there was no exception.

This is the thing that I see is hardly possible from a far away scope.
But fair enough for closely related code.


> It is, IMHO a multi though. There is no reason that every
> continuation cannot be a multi, because a continuation is just a
> sub.
>
> I don't know if there are method continuations - i guess there could
> be, but there's no need to permutate all the options when the
> options can compose just as well.

My view is that a (free) method type becomes a continuation
as follows:

1) the invocant type is determined
2) the dispatcher selects a matching target
3) this method maker object (like a class for constructing data objects)
is asked to create a not yet called invocation and bind it to the
invocant at hand
4) at that moment we have a not yet invoked sub instance, so
plain subs just start here
5) The signature is checked and bound in the environment of the calling
scope, the callers return continuation is one of the args
6) then this invocation is activated
7a) a return uses the return continuation in such a way that
the invocation is abandoned after the jump
b) a yield keeps the continuation just like a constructed object
and packages it as some kind of attachment to the return value.
BTW, I think there are more data types that use this technique
then one might think on first sight. Iteraters, junctions and
lazy evaluation in general come to mind.

Control structures are wrapped around their block tighter
and more efficient then the above procedure. In particular
the binding steps 3 and 5 are subject of optimizations.


> This is because the types of exceptions I would want to resume are
> ones that have a distinct cause that can be mined from the exception
> object, and which my code can unambiguously fix without breaking the
> encapsulation of the code that raised the exception.

Agreed. I tried to express the same above with my words.
The only thing that is a bit underspecced right now is
what exactly is lost in the process and what is not.
My guiding theme again is the type system where you leave
information about the things you need to be preserved to
handle unusual cicumstances gracefully---note: *not*
successfully, which would contradict the concept of exceptions!

Yuval Kogman

unread,
Sep 30, 2005, 1:55:40 PM9/30/05
to TSa, perl6-l...@perl.org
On Fri, Sep 30, 2005 at 17:09:23 +0200, TSa wrote:
> And it is the type system that guaranties the availability
> of the required information e.g. in $!.

$! is polymorphic.

Since CATCH is a topcializer, and you use when blocks to case over
$!, you can check to see if it has the metadata you want yourself:

CATCH {
when It::Is::An::Error::I::Can::Deal::With {
deal_with_error_of_kind_foo($!); # it may resume
}
}

> >Reentrancy is an implementation detail best left unmentioned.
>

> the reentrance proplem is just there

No, it's not - the solution is that $! is bound lexically to the
CATCH block. Exceptions that redefine $! keep the old value in the
exception stack inside of each exception, but this does not trample
any CATCH block. It'd be outright stupid to make

CATCH {
handle_error();
warn "$! was thrown within the code yadda";
}

print out completely bogus info becaue handle_error overwrote $!.

We can even deal with this in perl 5:

eval { bad_code };
if ($@) {
{
local $@;
code_that_might_die_in_a_handler;
}

warn "$! was thrown within bad_code";
}

> Just to synchronize our understanding, I see the following
> equivalences from the data and code domains
>
> data code
>
> class = sub
> instance = invocation

What?

> To illustrate my view consider

didn't help.

> The view I believe Yuval is harboring is the one examplified
> in movies like The Matrix or The 13th Floor and that underlies
> the holodeck of the Enterprise: you can leave the intrinsic
> causality of the running program and inspect it. Usually that
> is called debugging. But this implies the programmer catches
> a breakpoint exception or some such ;)

Err, no.

What I want is for perl 6 to help me write high quality user
interfaces easily.

One aspect of making this easy is letting me bridge between
exception throwing code and the UI layer using continuations.

> Exception handling is the programmatic automatisation of this
> process. As such it works the better the closer it is in time
> and context to the cause and the more information is preserved.

I read that as:

"As such it works the better the farther it is in time and
context from the UI code"

and I must disagree, but it is a matter of style.

As for information preserval:

* every object that inherits Exception can have an arbitrary
number of attributes.

* the base class Exception has code to make it be thrown,
or rethrown cleanly by making the previous value of $! available
as an attribute of the new value of $!

* Exception's constructor knows exactly where everything
happenned (for error reporting, *AND* resuming)

* a transition into a 'use fatal' lexical scope will cause the
throw method to be called on a value being returned, if it is an
exception.

* if the next statement to be executed is not inside a CATCH
block, the error object deletes it's .resume continuation, to
clean up the lexical scope that is still alive inside it. This
should be optional.

> But we all know that a usefull program is lossy in that respect.
> It re-uses finite resources during its execution. In an extreme
> setting one could run a program *backwards* if all relevant
> events were recorded!

That's a job for omniscient debuggers.

Languages with continuations are not debuggers.

Exceptions are not raised for every opcode, just to record the flow.
Exceptions normally do not happen.

> >Yes, even signals and exceptions.
> >The runtime is responsible for making these as fast as possible
> >without being unsafe.
>
> Hmm, I would see the type system in that role. It has all the
> information of the interested parties in a longjump. If it knows
> there are no potential handlers

The type system has nothing to do with code reentrency due to an
icky implementation that shares code.

Since malloc cannot be safely called within a C level signal
handler, the runtime needs to be responsible for setting the virtual
machine instruction pointer to the signal handling code, and to
mark a flag, and at the next opcode dispatch (no longer an unsafe
place) the signal handler will really happen.

This has nothing at all to do with the type system, and doesn't even
have anything to do with perl - this is parrot (or whoever)'s job.

> >It can't be a method because it never returns to it's caller - it's
>
> It beeing the CATCH block?

No, .resume.

I take it back, it could be a method that invokes a continuation.


> metric for dispatch applies. BTW, how is the signature of a CATCH
> block given? Simply

CATCH is just a topicalizer and a trait on the lexical scope.

Within it $! is topicalized, and you use when:

CATCH {
when rx/Permission denied/ { }
when Exception::Class { }
...
}

> >a continuation because it picks up where the exception was thrown,
>
> I would say it is given information about. In a way an exception
> handler is dispatched on the type of exception.

Again, you're confusing CATCH and .resume. CATCH is just a block of
code that is jumped to when there is an exception. It may be jumped
to because it's a continuation, but this is irrelevant.

.resume picks up where the exception was thrown - it is the
continuation that is the rest of the code after the 'die' or 'fail'
or whatever.

> This is the thing that I see is hardly possible from a far away scope.
> But fair enough for closely related code.

Since you know what kind of exception it is, you can know how to fix
it. If it's an IO::Open error, the solution is to give back an
alternative handle. If it's a Socket::Connect error the solution is
to find another way to connect to it.

If i'm writing a mail client I may want Socket::Connect to work
entirely different. There I have knowlege about what kind of
connection it is. In fact, I could write Socket::IMAP, which throws
a Socket::IMAP::Connect error, to help my exception handling code
cope better.

The exception handling code could display an error dialog saying
e.g. "Connection refused. Should I try an ssh tunnel instead?", and
if so, it could try to instantiate a Socket::IMAP::Tunneled and
return that instead.

However, Socket::IMAP might be a superclass that doesn't know about
Socket::IMAP::Tunneled, and neither should know about the UI code.

> My view is that a (free) method type becomes a continuation
> as follows:

Do you grok continuations? It doesn't look like it - you seem to
have confused things.

Let me illustrate. A coroutine, for example that has a yield:

sub foo {
for 1 .. 2 -> $x {
yield $x;
}

return "end";
}

say foo; # 1
say foo; # 2
say foo; # "end"
say foo; # 1
...

the way the coroutine works is that when it 'yield's a continuation
for the rest of the call to foo is stored, and the value is
returned.

On the next invocation of 'foo', the continuation is used instead -
the loop picks up right at the end when $x is still 1, and the body
of the loop ends. Then 2 is bound to $x and we yield again. foo is
invoked again and the new continuation is taken - right at the end
of the loop block. The loop has now finished, so we go to the next
statement: return "end". Return does not store a continuationand the
value is returned.

On the next invocation of 'foo', the continuation is used instead -
the loop picks up right at the end when $x is still 1, and the body
of the loop ends. Then 2 is bound to $x and we yield again. foo is
invoked again and the new continuation is taken - right at the end
of the loop block. The loop has now finished, so we go to the next
statement: return "end". Return does not store a continuation - the
next invocation of foo will start all over.

> 7a) a return uses the return continuation in such a way that
> the invocation is abandoned after the jump
> b) a yield keeps the continuation just like a constructed object
> and packages it as some kind of attachment to the return value.
> BTW, I think there are more data types that use this technique
> then one might think on first sight. Iteraters, junctions and
> lazy evaluation in general come to mind.

This is the part that convinced me there was confusion.

A coro macro could work something like this:

macro coro ($name, $pattern_tuple, &body) {
Perl6::Sub.new(
:name($name),
:pattern_tuple($pattern_tuple),
body => sub (*$a) {
state &coro_continuation;
if (defined &coro_continuation) {
return &coro_continuation.();
} else {
my &orig_return := &return;
temp our &yield = sub (*$a) {
&coro_continuation := &CALLER_CONTINUATION; # the code right after the yield();
orig_return(*$@);
};
temp &return = {
undefine(&coro_continuation);
orig_return(*$@);
}
}

&body.(*$a);

undfine(&coro_continuation);
},
)
}

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

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

Austin Hastings

unread,
Oct 1, 2005, 5:57:54 AM10/1/05
to Yuval Kogman, perl6-l...@perl.org
Yuval Kogman wrote:

>On Thu, Sep 29, 2005 at 13:52:54 -0400, Austin Hastings wrote:
>
>

[Bunches of stuff elided.]

>>A million years ago, $Larry pointed out that when we were able to use
>>'is just a' classifications on P6 concepts, it indicated that we were
>>making good forward progress. In that vein, let me propose that:
>>
>>* Exception handling, and the whole try/catch thing, IS JUST An awkward
>>implementation of (late! binding) run-time return-type MMD.
>>
>>
>
>Exception handling is just continuation passing style with sugar.
>
>Have a look at haskell's either monad. It has two familiar keywords
>- return and fail.
>
>
>Every statement in a monadic action in haskell is sequenced by using
>the monadic bind operator.
>
>The implementation of >>=, the monadic bind operator, on the Either
>type is one that first check to see if the left statement has
>failed. If it does, it returns it. If it doesn't it returns the
>evaluation of the right hand statement.
>
>Essentially this is the same thing, just formalized into a type....
>

Internally, it may be the same. But with exceptions, it's implemented by
someone other than the victim, and leveraged by all. That's the kind of
abstraction I'm looking for. My problem with the whole notion of "Either
errorMessage resultingValue" in Haskell is that we _could_ implement it
in perl as "Exception|Somevalue" in millions of p6 function signatures.
But I don't _want_ to. I want to say "MyClass" and have the IO subsystem
throw the exception right over my head to the top-level caller.

I guess that to me, exceptions are like aspects in they should be
handled orthogonally. Haskell's "Either" doesn't do that -- it encodes a
union return type, and forces the call chain to morph whenever
alternatives are added. The logical conclusion to that is that all subs
return "Either Exception or Value", so all types should be implicitly
"Either Exception or {your text here}". If that's so, then it's a
language feature and we're right back at the top of this thread.

>>Specifically, if I promise you:
>>
>> sub foo() will return Dog;
>>
>>and later on I actually wind up giving you:
>>
>> sub foo() will return Exception::Math::DivisionByZero;
>>
>>
>
>In haskell:
>
> foo :: Either Dog Exception::Math::DivisionByZero
>
>e.g., it can return either the expected type, or the parameter.
>
>Haskell is elegant in that it compromises nothing for soundness, to
>respect referential integrity and purity, but it still makes thing
>convenient for the programmer using things such as monads
>
>
>

For appropriate definitions of both 'elegant' and 'convenient'. Java
calls this 'checked exceptions', and promises to remind you when you
forgot to type "throws Exception::Math::DivisionByZero" in one of a
hundred places. I call it using a word to mean its own opposite: having
been exposed to roles and aspects, having to code for the same things in
many different places no longer strikes me as elegant or convenient.

>>the try/catch paradigm essentially says:
>>
>>I wanted to call <c>sub Dog foo()</c> but there may be times when I
>>discover, after making the call, that I really needed to call an anonymous
>><c>sub { $inner::= sub Exception foo(); $e = $inner(); given $e {...} }</c>.
>>
>>
>
>Yes and no.
>
>The try/catch mechanism is not like the haskell way, since it is
>purposefully ad-hoc. It serves to fix a case by case basis of out
>of bounds values. Haskell forbids out of bound values, but in most
>programming languages we have them to make things simpler for the
>maintenance programmer.
>
>

Right. At some level, you're going to have to do that. This to me is
where the "err" suggestion fits the most comfortably: "err" (or "doh!"
:) is a keyword aimed at ad-hoc fixes to problems. It smooths away the
horrid boilerplate needed for using exceptions on a specific basis.

do_something() err fix_problem();

is much easier to read than the current

{ do_something(); CATCH { fix_problem(); }}

by a lot. But only in two conditions: first that all exceptions are
identical, and second that the correct response is to suppress the
exception.

To me that fails because it's like "Candy Corn": you only buy it at
Halloween, and then only to give to other people's kids.

As syntactic sugar goes, it's not powerful enough yet.

>>We're conditionally editing the return stack. This fits right in with
>>the earlier thread about conditionally removing code from the inside of
>>loops, IMO. Once you open this can, you might as well eat more than one
>>worm. Another conceptually similar notion is that of AUTOLOAD. As a perl
>>coder, I don't EVER want to write
>>
>> say "Hello, world"
>> or die "Write to stdout failed.";
>>
>>-- it's "correct". It's "safe coding". And it's stupid for a
>>whole bunch of reasons, mostly involving the word "yucky".
>>
>>
>
>It's incorrect because it's distracting and tedious.
>
>http://c2.com/cgi/wiki?IntentionNotAlgorithm
>
>Code which does it is, IMHO bad code because obviously the author
>does not know where to draw the line and say this is good enough,
>anything more would only make it worse.
>
>

For instance, some bad coders :) wrote:

[quote="http://www.unix.org.ua/orelly/perl/cookbook/ch07_01.htm"]
... implicit closes are for convenience, not stability, because they
don't tell you whether the system call succeeded or failed. Not all
closes succeed. Even a |close| on a read-only file can fail. For
instance, you could lose access to the device because of a network
outage. It's even more important to check the |close| if the file was
opened for writing. Otherwise you wouldn't notice if the disk filled up.

close(FH) or die "FH didn't close: $!";

The prudent programmer even checks the |close| on standard output stream
at the program's end, in case STDOUT was redirected from the command
line the output filesystem filled up. Admittedly, your run-time system
should take care of this for you, but it doesn't.
[/quote]

The last sentence is telling, I think. The run-time system SHOULD take
as much care as possible. And rub my feet.


>The .resume continuation in the exception is equivalent to the call
>stack, as is any other continuation, implicit or explicit.
>
>
>

(Found while looking up Haskell docs, above.)

"The continuation that obeys only obvious stack semantics, O
grasshopper, is not the true continuation."
- Guy Steele

>>So <c>resume</c> is a multi, no? (Or it could just be a method:
>>$!.resume, but that doesn't read as well in a block that *really* should
>>be as readable as possible.)
>>
>>
>
>It can't be a method because it never returns to it's caller
>

True for any method that invokes exit(), no? Or that says NEXT on a
label outside its scope.

> - it's a continuation because it picks up where the exception was thrown,
>and returns not to the code which continued it, but to the code that
>would have been returned to if there was no exception.
>
>

Hmm, I'm thinking that the exception is an object that has access to a
continuation, and you get that access by invoking <c>resume $e;</c> more
or less.


>>Also, any layer of exception handling may do some nontrivial
>>amount of work before encountering its own exception:
>>
>>
>
>That's OK.
>
>
>
>>Which kind of argues that either (A) very fine control of RESUME blocks
>>must be available:
>>
>>
>
>just assume they work inside out when being resolved. This is just
>recursion into an alternative control flow, which is then stepped
>out of if there were as many .resumes as exceptions were thrown.
>There is no fundamental difference between exceptions and normal
>code once you accept continuations.
>
>

I was thinking of syntax.

>
>
>>{...
>> CATCH -> $e
>> {
>> do_some_work;
>> RESUME {...}
>>
>>
>
>I don't understand this at all.
>
>What does it mean? what exception is it?
>
>I think maybe this is what you wanted:
>
>{
> error_throwing_code;
> CATCH {
> do_some_work;
> CATCH {
> # the exception raised in the exception handler is
> # caught here
> }
> }
>}
>
>
>

Err, no.

The scenario is that I try something (error_throwing_code) and catch an
exception. Then while showing a dialog box to the user, for example, I
get another exception: not enough handles or whatever. So someone higher
than me resolves that on my behalf, then resumes me. I'm still trying to
resume the error thrown earlier:

{
error_throwing_code();
CATCH {
show_dialog_box();
# ...
}
}

Now I need to ask, what happens when show_dialog_box throws an
exception? Presumably, I don't catch it in this code path, or there will
be a stack fault shortly.

One possibility is that the catcher of an exception knows little or
nothing about the innards of the thrower. That's the normal, today,
Java/C++ case. Variables only scope inwards. But that pretty heavily
limits the things that can be done with exceptions. So maybe if there is
a resume, I can retry my operation:

{
error_throwing_code();
CATCH {
show_dialog_box();
CATCH {
throw; # Pass the exception
show_dialog_box(); # Retry if I get resumed?
}
}
}

That's going to get old, quickly.


>>sub copy_file($from, $to)
>>{
>> ...
>> RESUME -> $r
>> {
>> when Resumption::Microsoft::AbortRetryFail::Abort { last; }
>> when Resumption::Microsoft::AbortRetryFail::Retry { continue; }
>> when Resumption::Microsoft::AbortRetryFail::Fail { return FAILED; }
>> }
>>}
>>
>>I don't actually like this, because I don't like the whole notion of
>>upper-case blocks: it's stuff that the really good perl IDE is going to
>>hide so that the "real" code shows through.
>>
>>
>
>Exceptions are real code too - especially ones that are being dealt
>with.
>
>It's just slightly more aspect oriented in style.
>
>In fact, the whole point of AOP is to remove what they call cross
>cutting concerns (like logging, persistence, thread safety, etc)
>from the main code to keep it clean, without making the important
>support code invisible. They do this by adding what they call
>aspects from a third person perspective, into the control flow of
>the normal code, by specifying where the aspect gets control in a
>declarative style.
>
>
>

>>that someone can catch these errors at the relevant place and quickly
>>tailor them. The right place, of course, is the sub or method that is
>>doing the work. I want to be able to CATCH local exceptions locally,
>>too! Not because "I don't know what to do", but in a more Knuth/literate
>>sense, I don't want to write about what to do in-line with my beautiful
>>algorithm:
>>
>>sub greet
>>{
>> print "Hello, " # or throw PrintException;
>> print "world\n" # or throw PrintException;
>>
>> CATCH
>> {
>> when PrintException {...}
>> }
>>}
>>
>>But I want to know which PART of my elaborate greeting failed. In the p5
>>version I could discriminate between first-part failure and second-part
>>failures. Why has p6 backslid into mealy-mouthed genericity?
>>
>>
>
>It hasn't.. We still have 'or die' for raising exceptions, and you
>can use, like you mentioned
>
>
>
>>One way, Yuval's suggestion, is to make "err" be a quicky catch mechanism:
>>
>>
>
>It's not my suggestion, btw ;-)
>
>
>

Ah, sorry. I conflated the "err" thing with the Exceptuation thing. Mea
culpa.

>>sub greet
>>{
>> print "Hello, " err die "Could not write first part";
>> print "world!\n" err die "Could not write second part";
>>}
>>
>>But that's not abstracting anything. It just keeps the same, tedious,
>>check-every-result coding layout and gives it fractionally worse
>>Huffmanization.
>>
>>
>
>Well, there is a conflict here:
>
>you want to mark and not mark your errors at the same time.
>
>

Almost. I want exceptions, or exceptuations, to be sufficiently
transparent that I can use (below) some mechanism to infer details of
the problem without having to know too much of the internals below me
(but some, obviously); and I want all except(uat)ions to be potentially
resumable, except that some of them will override the resume
method/multi to avoid this.

But <c>resume</c> should be part of the Exception interface.

>
>
>>What I want is to catch my errors elsewhere, and still
>>be able to localize their solution. Cleverly, there's already a way to
>>do this: labels. All I need is to know where I'm at, label-wise, and
>>I've got a dandy way to mark my code up.
>>
>>
>
>This is feasable... I don't see why it shouldn't be implemented, but
>frankly I don't see myself using it either.
>
>This is because the types of exceptions I would want to resume are
>ones that have a distinct cause that can be mined from the exception
>object, and which my code can unambiguously fix without breaking the
>encapsulation of the code that raised the exception.
>
>

Okay. So one way is to include a continuation in the exception. That
lets you resume, if the exception type is one you know. It lets me
inspect the code tree or call stack or whatever and process it in a
possibly invasive way, possibly resuming. I think we're in sync on this one.

>These are all very specific and reusable, regardless of what we're
>resuming. This is nice because especially if we're trying to e.g.
>
> my @handles = map { open $_ } @file_names;
>
>we can let the user skip unreadable files, by using return with no
>value instead of returning a handle. This is a generic pattern that
>enhances the user experience by not forcing the user to start over
>again.
>
>For more specific code, i doubt there will be several exceptions of
>the same kind in a call chain.
>
>

What's your example there? The "err undef" fashion, or the "pops up a
dialog box and then calls setuid()"?

I confess I find that (the dialog box) an amazing piece of work, but in
practical terms somebody someplace has to be doing a retry: I don't see
how it would work via resuming an exception.


>>[quote=http://java.sun.com/docs/books/tutorial/essential/exceptions/finally.html]
>>The final step in setting up an exception handler is to clean up before
>>allowing control to be passed to a different part of the program.
>>
>>
>
>For cleanup we have real useful garbage collection, with a
>declarative interface to decide on how an object needs to disappear.
>
>Resource management is the task of the resource creator, not
>exception handler, IMHO. It's just too hard to get it right.
>
>So your point:
>
>
>
>>But resumptions are going to let us FIX the problem that occurred when
>>we tried to open the 256th filehandle. So all the directory parsing,
>>file exploring, and whatnot is going to be *preserved*.
>>
>>
>
>is very valid
>
>
>
>>Thus we don't
>>actually want to automatically finalize all that stuff unless we know
>>for sure that we aren't going to resume: even *later* binding.
>>
>>
>
>Continuations give this behavior consistently.
>
>They just need to be garbage collected themselves, in a way.
>
>What I would like to disallow is the ability to take $! and just
>.resume it out of the blue in code that isn't an exception handler.
>That just does't make sense:
>
> try { ... }
> some_other_stuff;
> $!.resume if $!;
>
>

Unless Damian does it ;)

>>Yet another use for a Pascal's <c>with</c> keyword:
>>
>>
>
>I actually quite liked that RFC =)
>
>The problem is that records in a static language are static, and
>hashes are dynamic.
>
>There is no way to tell the user at compile time that a variable
>doesn't exist because we don't know if there's such a hash key.
>
>This means that 'use strict' cannot work in a 'with'ed block.
>
>
>

This is, to me, different. It's a "discontinuation". The exception
handler is basically stripping the "ation" from the "continue", briefly.
My suggestion here is that <c>with</c> load the dataspace of the
continuation atop the current environment.

The more I think about it, though, the harder it seems. It makes sense
when there's no encapsulation: when you're handling exceptions from
within you own package and don't mind updating member data or whatever.
But for things like smartening up the map {open} example you gave, the
requirement for opacity makes anything more than the "err" suggestion
impractical.

=Austin


Austin Hastings

unread,
Oct 1, 2005, 6:33:00 AM10/1/05
to TSa, perl6-l...@perl.org
TSa wrote:

>
> The view I believe Yuval is harboring is the one examplified
> in movies like The Matrix or The 13th Floor and that underlies
> the holodeck of the Enterprise: you can leave the intrinsic
> causality of the running program and inspect it. Usually that
> is called debugging. But this implies the programmer catches
> a breakpoint exception or some such ;)
>
> Exception handling is the programmatic automatisation of this
> process. As such it works the better the closer it is in time
> and context to the cause and the more information is preserved.
> But we all know that a usefull program is lossy in that respect.
> It re-uses finite resources during its execution. In an extreme
> setting one could run a program *backwards* if all relevant
> events were recorded!
>

The current state of the art dictates that exceptions are to be avoided
when it is possible to handle the error in-line. That "exceptions should
only be used for exceptional cases, and anything you encounter in the
manual pages is not exceptional."

I don't agree with this, because it is IMO effectively saying "We had
this powerful notion, but it turned out to be difficult to integrate
post-hoc into our stack-based languages, so we're going to avoid it.
Rather than admitting defeat, though, we're going to categorize it as
some kind of marginal entity."

I don't see exceptions as necessarily being outside the intrinsic
causality of the running program. They are non-traditional forms of flow
control: event-based programming, if you will, in an otherwise
sequential program.

We do much the same thing when we talk about coroutines: violate the
traditional stack model. We do the same thing again when we talk about
aspects: de-localize processing of certain (ahem) aspects of the problem
domain. The telling part of aspects, though, was the the first popular
implementation (AspectJ) required a preprocessor and a special markup
language to implement. Why? Because nobody uses extensibility and Java
in the same sentence. I guess aspects are traditional in that regard,
though: remember CFront. Perl, OTGH, doesn't have the poor body-image or
whatever it is that keeps people afraid to change the syntax.

>> It can't be a method because it never returns to it's caller - it's
>
>
> It beeing the CATCH block?


Ahh, no. "It" in this case is the ".resume" call. My question was is
<c>resume</c> a multi, an object method, or what?

>> This is because the types of exceptions I would want to resume are
>> ones that have a distinct cause that can be mined from the exception
>> object, and which my code can unambiguously fix without breaking the
>> encapsulation of the code that raised the exception.
>
>
> Agreed. I tried to express the same above with my words.
> The only thing that is a bit underspecced right now is
> what exactly is lost in the process and what is not.
> My guiding theme again is the type system where you leave
> information about the things you need to be preserved to
> handle unusual cicumstances gracefully---note: *not*
> successfully, which would contradict the concept of exceptions!


This is the classical view of exceptions, and so it is subject to the
classical constraints: you can't break encapsulation, so you can't
really know what's going when the exception occurs.

The reason I like the "with" approach is that it lets us delocalize the
processing, but does _not_ keep the "exceptions are violent,
incomprehensible events which wrench us from our placid idyll" mentality.

In that regard, exceptuations are "resumable gotos".

=Austin

Yuval Kogman

unread,
Oct 1, 2005, 7:55:16 AM10/1/05
to Austin Hastings, perl6-l...@perl.org
On Sat, Oct 01, 2005 at 05:57:54 -0400, Austin Hastings wrote:

> Internally, it may be the same. But with exceptions, it's implemented by
> someone other than the victim, and leveraged by all. That's the kind of
> abstraction I'm looking for. My problem with the whole notion of "Either
> errorMessage resultingValue" in Haskell is that we _could_ implement it
> in perl as "Exception|Somevalue" in millions of p6 function signatures.
> But I don't _want_ to. I want to say "MyClass" and have the IO subsystem
> throw the exception right over my head to the top-level caller.

In haskell it's the job of the Either monad to let you pretend you
aren't doing Exception|Somevalue everywhere.

You can sequence operations in a deeply nested manner, and then
'fail' at some point. Then control flow will just pop back up all
the way with the error, instead of trying to continue.

You don't really need to say 'Either ... ...', you just use do
notation.

> For appropriate definitions of both 'elegant' and 'convenient'. Java
> calls this 'checked exceptions', and promises to remind you when you
> forgot to type "throws Exception::Math::DivisionByZero" in one of a
> hundred places. I call it using a word to mean its own opposite: having
> been exposed to roles and aspects, having to code for the same things in
> many different places no longer strikes me as elegant or convenient.

I agree with that wholeheartedly, but in haskell you are making no
obligation towards the shape of an exception - it can be 'Either
thing Error' where Error is any data type you like.

In this sense haskell is just as flexible but requires more
abstraction than perl etc. It has it's merits - it's safer, and more
reusable. It tends to win ICFP contests, and so forth.

However, to just get that thing working real fast, without having
to pay too much when the context becomes maintenance instead of
development, i think Perl 6 will be the most suitable language in
the world.

> Right. At some level, you're going to have to do that. This to me is
> where the "err" suggestion fits the most comfortably: "err" (or "doh!"
> :) is a keyword aimed at ad-hoc fixes to problems. It smooths away the
> horrid boilerplate needed for using exceptions on a specific basis.

Which is why it's such a nice propsal =)

> As syntactic sugar goes, it's not powerful enough yet.

err next # catch all
err Pattern, next # catch some

Putting my code where my mouth is:

sub infix:<err> (&lhs is delayed, Pattern ?$match = Any, &rhs is delayed) {
lhs;
CATCH {
when $match { rhs }
default { die }
}
}

Ofcourse, these can also stack:

my $fh = open "file
err rx/Permission Denied/, next
err rx/No such file/, die;

But i don't think this is very useful for one or at the very most
two catches - for anything else it's ad-hoc nature just doesn't
scale as nicely as CATCH blocks, which can be applied to several
error generating blocks of code at once.

Ofcourse, true acid heads can always say:

do {
...;
...;
} err ..., ...
err ..., ...;

but that's their problem. =)

> The last sentence is telling, I think. The run-time system SHOULD take
> as much care as possible. And rub my feet.

Yes =)

> True for any method that invokes exit(), no? Or that says NEXT on a
> label outside its scope.

Well, that's a semantic detail. The issue is that those methods
*can* return, but don't.

A continuation will never return - because it already has another
place to return - the place that created it.

This is ignoring CPS, ofcourse, in which every return and every call
is a continuation. While this may be true under the hood, this is
not what the average Perl 6 user can observe.

> The scenario is that I try something (error_throwing_code) and catch an
> exception. Then while showing a dialog box to the user, for example, I
> get another exception: not enough handles or whatever. So someone higher
> than me resolves that on my behalf, then resumes me. I'm still trying to
> resume the error thrown earlier:

Yes, that should work.

> Now I need to ask, what happens when show_dialog_box throws an
> exception? Presumably, I don't catch it in this code path, or there will
> be a stack fault shortly.

If the exception from show_dialog_box was thrown, and another CATCH
handler fixed it for you, you don't need to worry about it - you can
never know because you don't get access to that exception. It's as
if it was never propagated.

> One possibility is that the catcher of an exception knows little or
> nothing about the innards of the thrower.

It's the job of exception classes to bridge these - they have a
class, and any number of attributes.

Exception::IO::Open::PermissionDenied

In fact I suspect that Exception::IO::Open enforces a certain type of
fix, too:

class Exception::IO::Open is Exception {
has $.path; # what we tried to open
has $.code; # the OS error code
has $.mode; # the mode we attempted to open it with

...

method resume (IO::Handle $thing) {
$?SELF.SUPER::resume($thing);
}
}


That way any bit of code that does:

CATCH {
when Exception::IO::Open {
..
}
}

has documented, encapsulation safe tools to fix IO related errors,
by redoing the call to open after remedying the situation.

It doesn't need to know what code actually called open().

Nowadays to get "standard" behavior from a UI toolkit would you have
to break abstraction and say

My::Toolkit::open(...);

to get semantics like a standard administrative access panel.

With continuable exceptions you just use plain open, and the UI code
can wrap your entire app with a rich standard library of exception
handlers.

> {
> error_throwing_code();
> CATCH {
> show_dialog_box();
> CATCH {
> throw; # Pass the exception
> show_dialog_box(); # Retry if I get resumed?
> }
> }
> }
>
> That's going to get old, quickly.

Just

{
error_throwing_code();
CATCH {
show_dialog_box();
}
}

since the parent CATCH will resume from *inside* show_dialog_box.

The abstraction of continuable exceptions should cascade.

> But <c>resume</c> should be part of the Exception interface.

Aye, see usefulness of this in above example.

> >resuming. This is nice because especially if we're trying to e.g.
> >
> > my @handles = map { open $_ } @file_names;
> >
> >we can let the user skip unreadable files, by using return with no
> >value instead of returning a handle. This is a generic pattern that
> >enhances the user experience by not forcing the user to start over
> >again.
> >
> >For more specific code, i doubt there will be several exceptions of
> >the same kind in a call chain.
> >
> >
> What's your example there? The "err undef" fashion, or the "pops up a
> dialog box and then calls setuid()"?

An exception ahndler that pops up a dialog box.

In this case it would be:

The file <x> could not be read: $!

chmod it (chmod the file, and reopen it with same args)

skip it (resume with no value)

abort the program (rethrow instead of resume)

Now, if the chmod operation had an exception like permission denied,
obviously we are not the owner. To chown the file we need to setuid,
and for that we need the user to authenticate.

> I confess I find that (the dialog box) an amazing piece of work, but in
> practical terms somebody someplace has to be doing a retry: I don't see
> how it would work via resuming an exception.

That's the point of continuable exceptions. When returning an
exception from 'open' which is then thrown due to 'use fatal', the
exception handler can then resume from the point where it was
thrown, effectively replacing the exception object with a handle,
that is then assigned to the variable:

my $handle = open "file";

Under 'no fatal' $handle will have an exception object

under 'use fatal' $handle will not be set at all... Unless the
exception handler provides a different value to be put into $handle.

> Unless Damian does it ;)

I doubt even Damian likes to have all the lexical scopes his program
made boo-boos in stay alive with no GC in case someone at some later
point in the program decides that they can fix those errors.

Also - after the code happenned, certain blocks of code will be
repeated.

This might be useful for obfuscation though ;-)

> My suggestion here is that <c>with</c> load the dataspace of the
> continuation atop the current environment.

Yes, I see, but instead of doing it that way you can do it more
safely with more characters by introspecting the exception object
and using well documented attributes instead.

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

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

Nicholas Clark

unread,
Oct 18, 2005, 6:06:22 AM10/18/05
to Adam D. Lopresto, perl6-l...@perl.org
On Wed, Sep 28, 2005 at 11:46:37AM -0500, Adam D. Lopresto wrote:

> [2] I've actually seen data lost due to this. When drive space is very
> limited
> (due to, for instance, a user quota) it's often possible to open a new file
> (since
> there's some space left), but the close fails since too much was written to
> it.

Likewise. A certain reputable OS vendor's NFS implementation went
multithreaded, with the result that close() was now where the over quota
error was reported, rather than the individual writes.

Said reputable OS vendor's own C compilers weren't checking close(), so
they were caught out by this too.


So I think that the problem is real, and is worthy of addressing.
I'm not sure of the best approach, or syntax, or defaults.

Nicholas Clark

Dave Mitchell

unread,
Oct 18, 2005, 7:56:09 AM10/18/05
to Adam D. Lopresto, perl6-l...@perl.org
On Tue, Oct 18, 2005 at 11:06:22AM +0100, Nicholas Clark wrote:
> Likewise. A certain reputable OS vendor's NFS implementation went
> multithreaded, with the result that close() was now where the over quota
> error was reported, rather than the individual writes.
>
> Said reputable OS vendor's own C compilers weren't checking close(), so
> they were caught out by this too.

If it's the vendor I'm thinking of, their compress(1) command didn't check
for close() errors either. The net result of this was that when my NFS users
went over quota, then tried to make space by compressing some files, they
ended up with lots of zero-sized foo.Z files with the original foo deleted.
They were not happy bunnies.

--
Technology is dominated by two types of people: those who understand what
they do not manage, and those who manage what they do not understand.

0 new messages