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!
pgpYXFFGrPghq.pgp
Description: PGP signature