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