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 <[EMAIL PROTECTED]> 0xEBD27418  perl hacker &
 /\  kung foo master: /me does not drink tibetian laxative tea: neeyah!

Attachment: pgpYXFFGrPghq.pgp
Description: PGP signature

Reply via email to