Re: Exceptuations
Yuval Kogman wrote: On Thu, Oct 06, 2005 at 14:27:30 -0600, Luke Palmer wrote: On 10/6/05, Yuval Kogman [EMAIL PROTECTED] wrote: when i can't open a file and $! tells me why i couldn't open, i can resume with an alternative handle that is supposed to be the same when I can't reach a host I ask a user if they want to wait any longer when disk is full I ask the user if they want to write somewhere else when a file is unreadable i give the user the option to skip I'm not bashing your idea, because I think it has uses. But I'll point out that all of these can be easily accompilshed by writing a wrapper for open(). That would be the usual way to abstract this kind of thing. Writing a wrapper may be the implementation mechanics: sub safe_open() { call; CATCH { when E::AccessDenied { return show_user_setuid_dialog(); }} } open.wrap(safe_open); But this is just one way to do it, and it fails to provide for helping other people's code: Yuval's GUI environment would offer to fix the problem for ALL file related calls (open, dup, popen, ad nauseum), and would not have to worry about the order in which calls are wrapped. But see below. Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this. Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option. I think that what your proposal *really* requires is uniformity. There are other ways to get the same behavior, including an abstract factory interface for exception construction (would provide virtual constructor for exceptions, so permitting userspace to insert a 'retry' behavior), but it has the same vulnerability: the p6core must cooperate in uniformly using the same mechanism to report errors: throw, fail, die, error, abend, whatever it's eventually called. sub *::throw(...) { return recover_from_error([EMAIL PROTECTED]) or P6CORE::throw([EMAIL PROTECTED]); } =Austin
Re: Exceptuations
On Fri, Oct 07, 2005 at 02:31:12 -0400, Austin Hastings wrote: Yuval Kogman wrote: On Thu, Oct 06, 2005 at 14:27:30 -0600, Luke Palmer wrote: On 10/6/05, Yuval Kogman [EMAIL PROTECTED] wrote: when i can't open a file and $! tells me why i couldn't open, i can resume with an alternative handle that is supposed to be the same when I can't reach a host I ask a user if they want to wait any longer when disk is full I ask the user if they want to write somewhere else when a file is unreadable i give the user the option to skip I'm not bashing your idea, because I think it has uses. But I'll point out that all of these can be easily accompilshed by writing a wrapper for open(). That would be the usual way to abstract this kind of thing. Writing a wrapper may be the implementation mechanics: sub safe_open() { call; CATCH { when E::AccessDenied { return show_user_setuid_dialog(); }} } open.wrap(safe_open); But this is just one way to do it, and it fails to provide for helping other people's code: Yuval's GUI environment would offer to fix the problem for ALL file related calls (open, dup, popen, ad nauseum), and would not have to worry about the order in which calls are wrapped. But see below. Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this. Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option. I think that what your proposal *really* requires is uniformity. There are other ways to get the same behavior, including an abstract factory interface for exception construction (would provide virtual constructor for exceptions, so permitting userspace to insert a 'retry' behavior), but it has the same vulnerability: the p6core must cooperate in uniformly using the same mechanism to report errors: throw, fail, die, error, abend, whatever it's eventually called. We have: die: throw immediately fail: return an unthrown exception, which will be thrown depending on whether our caller, and their caller - every scope into which this value propagates - is using fatal. This is enough for normal exception handling. As for recovery - the way it's done can be specialized on top of continuations, but a continuation for the code that would run had the exception not been raised is the bare metal support we need to do this. Where this gets taken further by (IMHO overly abstract) exception handling libraries and standardization is a question that application development kits (e.g. Catalyst, GTK, Cocoa) must develop a policy for. return recover_from_error([EMAIL PROTECTED]) what does this do? -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me beats up some cheese: neeyah! pgp3FUsw93RD9.pgp Description: PGP signature
Re: Exceptuations
On Fri, Oct 07, 2005 at 05:23:55 +0100, Piers Cawley wrote: Peter Haworth [EMAIL PROTECTED] writes: On Wed, 5 Oct 2005 19:24:47 +0200, Yuval Kogman wrote: On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote: On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. No! Not well said at all! Sorry, I misread that. I thought I was agreeng with how does a higher level exception catcher know what to change in order to make resuming the continuation useful?, especially in the light of Piers saying that the bottom-most exception should be the one resumed. I'm sorry, we appear to have lost some kind of context, the original example given only had one exception thrown, but it got propagated up through a long call chain. At no point did anything catch the original exception and rethrow. If they had, you're absolutely correct in asserting that by default things should resume from the point of the outermost rethrow. A brave exception catcher (or more likely programmer with a debugger) might want to crack that exception open and examine its inner exceptions, but in general that's not going to be safe. It doesn't really matter since 'fail'ed exceptions will simply be converted to a return with the continued value when resumed, and the question of outer/inner scopes is really irrelevant - it's like tail calls. As for die - since there is no implicit returning in die, it might or might not make sense to try and resume. However, for most situations it still looks to me like 'die foo' could be treated as return in a way. Essentially throwing an error means that the function/method who threw it is giving up since it doesn't know how to resume. If the exception handler can find an alternative for that function, and replace the value, it's immediate caller should get the fixed value, since they are the ones who need the value. Functions which delegate exceptions are, again, just like tail calls. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me wields bonsai kittens: neeyah pgpjB4t4GbwlH.pgp Description: PGP signature
Re: Exceptuations
[EMAIL PROTECTED] wrote: I'm not bashing your idea, because I think it has uses. But I'll point out that all of these can be easily accompilshed by writing a wrapper for open(). That would be the usual way to abstract this kind of thing. My take on this: resumable exceptions break encapsulation no more (and no less) than using callbacks. The function that throws a resumable exception can only do this knowingly, and it could just as well offer a callback for that specific patchable error. So why take away a potentially highly useful tool? BTW, two languages I know of have resumable exceptions: Common LISP and TOM. Neither supports continuations, instead, they separate raising exception (which runs a handler; this is implemented as a callback) from unwinding stack (which happens when the handler actually throws; it can choose to return instead, resuming execution if the raising function is okay with it). At least in Common LISP this is used a *lot* during interactive development, as it allows the developer to quickly patch things up without reruning the failed test. Assuming perl6 keeps the pugs-style interactive shell, I suspect resumable exceptions will be quickly added into the standard library if they don't pass into the standard. Yes, they're that useful. Miro
Re: Exceptuations (proposal for RESUME blocks)
HaloO Yuval Kogman wrote: We have: die: throw immediately fail: return an unthrown exception, which will be thrown depending on whether our caller, and their caller - every scope into which this value propagates - is using fatal. This is enough for normal exception handling. Is it only me seeing a self-contradiction in 'normal exception'? But here are some ideas for renaming: resumeable exception - retro-fitted pre-condition or: negotiated pre-condition or sumply resumption? My mental picture is that of an outer scope in the call chain attempting to meet the requirements communicated from an unknown inner scope and then resuming where the failed pre-condition was spotted. But I wouldn't require this to be implemented in a very efficient way! When you need event handling, implement it. E.g. the fail form needs to preserve the call chain until the failure is handled. Handling might be as easy as evaluation in void context or usage of // in a non-fatal scope. Otherwise a CATCH block is needed. Or perhaps a RESUME block and a correspondingly enhanced require form? That would integrate the AUTODEF and friends with the error/exception/control/assertion system. To illustrate the idea: if !$file.writeable { require $file.writeable; } or perhaps even without the if. And then somewhere up the call chain RESUME { when .writeable { make $! writeable; resume } # more typeish syntax when File::writeable { make $! writeable; resume } } # with type outside RESUME File::writeable { make $! writeable; resume } The resume could actually be the default way to exit a RESUME block. As for recovery - the way it's done can be specialized on top of continuations, but a continuation for the code that would run had the exception not been raised is the bare metal support we need to do this. Yep, we need to agree on a form how to communicate unmet requirements outwards. The only entity that comes to my mind that usually matches requirements is the type system or meta information repository. Where this gets taken further by (IMHO overly abstract) exception handling libraries and standardization is a question that application development kits (e.g. Catalyst, GTK, Cocoa) must develop a policy for. Well, yes! A non-trivial framework will install a bunch of types. A subset of which beeing exception (sub)types. The challenge is then shifted to inter-framework unification =8) But one thing that should work is tunneling standard resumption requests through such a framework. -- $TSa.greeting := HaloO; # mind the echo!
Re: Exceptuations
Yuval Kogman wrote: On Fri, Oct 07, 2005 at 02:31:12 -0400, Austin Hastings wrote: Yuval Kogman wrote: Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this. Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option. I think that what your proposal *really* requires is uniformity. There are other ways to get the same behavior, including an abstract factory interface for exception construction (would provide virtual constructor for exceptions, so permitting userspace to insert a 'retry' behavior), but it has the same vulnerability: the p6core must cooperate in uniformly using the same mechanism to report errors: throw, fail, die, error, abend, whatever it's eventually called. We have: die: throw immediately fail: return an unthrown exception, which will be thrown depending on whether our caller, and their caller - every scope into which this value propagates - is using fatal. This is enough for normal exception handling. Yet here we are discussiong abnormal exception handling. As for recovery - the way it's done can be specialized on top of continuations, but a continuation for the code that would run had the exception not been raised is the bare metal support we need to do this. No, it isn't. It's *one way* to do this. Any mechanism which transfers control to your error-recovery code in such a way that can cause an uplevel return of a substituted value is the bare metal support we need. You're conflating requirements and design. I suggested an alternative *design* in my prior message, to no avail. Try this instead: You overload the global 'die' with a sub that tries to decode the error based on its arguments. If it cannot comprehend the error, it invokes P6CORE::die(). If it comprehends the error, it tries to resolve it (querying the user, rebooting the machine to free up space in /tmp, whatever) and if successful it returns the fixed value. But wait! This requires that everyone do return die ... instead of die..., and we can't have that. So you add a source filter, or macro, or tweak the AST, or perform a hot poultry injection at the bytecode level, or whatever is required to convert die into return die whereever it occurs. Et voila! No exceptions are caught, no continuations are released into the wild. And yet it flies. Much like the hummingbird it looks a little awkward, and I'm way not sure that munging bytecodes is necessarily a better idea. But the point is that resuming from an exception (or appearing to) is not bound to implemented with continuations. =Austin Et vidi quod aperuisset Autrijus unum de septem sigillis, et audivi unum de quatuor animalibus, dicens tamquam vocem tonitrui : Veni, et vide. Et vidi : et ecce camelus dromedarius, et qui scriptat super illum, habebat archivum sextum, et data est ei corona, et exivit haccum ut vinceret. Apocalypsis 6:1 (Vulgata O'Reilly)
Re: Exceptuations
On Fri, Oct 07, 2005 at 10:28:13 -0400, Austin Hastings wrote: But the point is that resuming from an exception (or appearing to) is not bound to implemented with continuations. What's the point? Continuations are good for exactly this purpose. Parrot already supports them. I see absolutely no reason why we would want to implement this any other way but using continuations. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: *shu*rik*en*sh*u*rik*en*s*hur*i*ke*n*: neeyah pgpRP3mAuY8ah.pgp Description: PGP signature
Re: Exceptuations
On Wed, Oct 05, 2005 at 13:00:55 -0600, Luke Palmer wrote: I don't think it was a how is this possible, but more of a what business does it have?. And as far as I gathered, they're saying pretty much what you've been saying, but in a different way. It's about the continuation boundary; that is, if you're outside a module, you have no say in how the module does its business. You can continue only at the module boundary, replacing a return value from its public interface. As I see it this is the usefulness of exceptions being handled by distant code. The code in the module inverts it's interface by calling code it doesn't know with a certain parameter, accepting a certain parameter back. That way encapsulation is not broken, but errors that happen deep inside a call chain can be dealt with by code that can interact with the user. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me climbs a brick wall with his fingers: neeyah! pgpmp8WUodYYB.pgp Description: PGP signature
Re: Exceptuations
On Wed, Oct 05, 2005 at 13:41:37 -0700, Dave Whipp wrote: Reading this thread, I find myself wondering how a resumable exception differs from a dynamically scropted function. Imagine this code: This is sort of like what I mean, except that there is no encapsulation breakage, since the interface is inverted. The advantage of this approach is that error handling code in a submodule can benefit from generic, reusable exception handling code that is provided by it's caller. temp sub FileNotWriteable( Str $filename ) { With an exception handler and continuable exceptions you don't have to know what the error handler is, or make sure that the module actually throws the error. The exception handler instead deals with the type of the exception in a generic manner (it doesn't care when the exception was actually generated). The module doesn't need to throw the error it just needs to fail (or delegate a fail), until the failure crosses into a 'use fatal' scope. That way both the catching code and the throwing code are reusable and orthogonal when they are unrelated, but the possibility of coupling handling code with throwing code is still there. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me groks YAML like the grasshopper: neeyah!! pgpzo38NCmZ47.pgp Description: PGP signature
Re: Exceptuations
On Wed, 5 Oct 2005 19:24:47 +0200, Yuval Kogman wrote: On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote: On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. No! Not well said at all! Sorry, I misread that. I thought I was agreeng with how does a higher level exception catcher know what to change in order to make resuming the continuation useful?, especially in the light of Piers saying that the bottom-most exception should be the one resumed. The highest level exception is the only one a caller has any right to deal with, but even then it doesn't really know what will happen if it resumes some random continuation attached to the exception. CATCH { when some_kind_of_error { $!.continue($appropriate_value_for_some_kind_of_error) } } That just gives me the willies, I'm afraid. -- Peter Haworth [EMAIL PROTECTED] Disconcerting Haworth Fortress Unicycling Melody Gundam -- http://www.channel4.com/4later/bits/manga.html
Re: Exceptuations
On Thu, Oct 06, 2005 at 18:11:38 +0100, Peter Haworth wrote: The highest level exception is the only one a caller has any right to deal with, but even then it doesn't really know what will happen if it resumes some random continuation attached to the exception. But then we gain nothing CATCH { when some_kind_of_error { $!.continue($appropriate_value_for_some_kind_of_error) } } That just gives me the willies, I'm afraid. Why? when i can't open a file and $! tells me why i couldn't open, i can resume with an alternative handle that is supposed to be the same when I can't reach a host I ask a user if they want to wait any longer when disk is full I ask the user if they want to write somewhere else when a file is unreadable i give the user the option to skip These handlers are progressively more knowlegable of the code they're dealing with, but they don't touch the actual guts - that's the whole merit of continuable exception, because otherwise you might aswell just start over. These are 100% reusable in the first exception handler, and not reusable but still applicable to an opaque call at the last handler. It doesn't matter who opens the file file, the exception handler will produce the same effect but in a different way. CATCH { when Error::IO::... { open ... } when MyApp::Timeout { ask_user_to_continue_or_abort(); } ... } -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me dodges cabbages like macalypse log N: neeyah! pgpQfW4BcQBM2.pgp Description: PGP signature
Re: Exceptuations
On 10/6/05, Yuval Kogman [EMAIL PROTECTED] wrote: when i can't open a file and $! tells me why i couldn't open, i can resume with an alternative handle that is supposed to be the same when I can't reach a host I ask a user if they want to wait any longer when disk is full I ask the user if they want to write somewhere else when a file is unreadable i give the user the option to skip I'm not bashing your idea, because I think it has uses. But I'll point out that all of these can be easily accompilshed by writing a wrapper for open(). That would be the usual way to abstract this kind of thing. Luke
Re: Exceptuations
On Thu, Oct 06, 2005 at 14:27:30 -0600, Luke Palmer wrote: On 10/6/05, Yuval Kogman [EMAIL PROTECTED] wrote: when i can't open a file and $! tells me why i couldn't open, i can resume with an alternative handle that is supposed to be the same when I can't reach a host I ask a user if they want to wait any longer when disk is full I ask the user if they want to write somewhere else when a file is unreadable i give the user the option to skip I'm not bashing your idea, because I think it has uses. But I'll point out that all of these can be easily accompilshed by writing a wrapper for open(). That would be the usual way to abstract this kind of thing. Stylistically I would tend to disagree, actually. I think it's cleaner to use exception handling for this. Also, this implies that you know that the errors are generated by open. This is OK for open(), but if the errors are generated from a number of variants (MyApp::Timeout could come from anywhere in a moderately sized MyApp), then wrapping is not really an option. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me groks YAML like the grasshopper: neeyah!! pgppFkb4LL0f1.pgp Description: PGP signature
Re: Exceptuations
Peter Haworth [EMAIL PROTECTED] writes: On Wed, 5 Oct 2005 19:24:47 +0200, Yuval Kogman wrote: On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote: On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. No! Not well said at all! Sorry, I misread that. I thought I was agreeng with how does a higher level exception catcher know what to change in order to make resuming the continuation useful?, especially in the light of Piers saying that the bottom-most exception should be the one resumed. I'm sorry, we appear to have lost some kind of context, the original example given only had one exception thrown, but it got propagated up through a long call chain. At no point did anything catch the original exception and rethrow. If they had, you're absolutely correct in asserting that by default things should resume from the point of the outermost rethrow. A brave exception catcher (or more likely programmer with a debugger) might want to crack that exception open and examine its inner exceptions, but in general that's not going to be safe. The scary syntax proposed above is, again, the sort of thing that might be useful in a debugger I don't really care about the inner workings of these helper functions, I just want 'open' to return this mocked handle. (actually in that case, being able to do $!.caller(open).continue(MockIO.new), where 'caller open' looks up the call chain for the lowest call to open and returns that continuation would be rather neat) The highest level exception is the only one a caller has any right to deal with, but even then it doesn't really know what will happen if it resumes some random continuation attached to the exception. Oh stop with the 'rights'. And it's not dealing with a 'random' continuation, if it's going to resume it should be damned careful about which exceptions it resumes from; you don't just go around doing CATCH {...; $!.continue(...)}, you do CATCH SomeSpecificKindOfResumableException { ...; $!.continue(...)}. And, in general, you don't do that either, because in the average program you catch the exception at a point where you can simply return a sensible default to your caller. Resumable exceptions come into their own, however, when you're debugging. I can envisage doing: perl6 -debug::on::error some_script And have it run along happily until an exception gets propagated up to the top level, at which point the debugger swings into action and uses the continuation to tunnel back to the point at which the exception was thrown so the programmer can inspect the program state, possibly fix things up, return something sensible and carry on. CATCH { when some_kind_of_error { $!.continue($appropriate_value_for_some_kind_of_error) } } That just gives me the willies, I'm afraid. It doesn't amuse me that much, unless whatever generates $appropriate_value_for_some_kind_of_error is very, very smart indeed. But, as I've said above, that's not really where resumable exceptions shine. -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Exceptuations
On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. Think of receiving a 'shelf picker died of lung cancer' exception when you just ordered a book from your favorite book dealer. Irrespective of the seriousness to the shelf picker, but how would you expect a customer to handle such an exception? This kind of exception should never get so far up the call chain, except possibly as a nested cause of a more general exception. The customer is in no position to get the book dealer to choose a different stock picker - that's up to the order picking department of the book shop. If it doesn't get handled there, then maybe the book shop can try sourcing the book externally, and if that fails, they may defer your order until later. The shop could ask the customer's opinion as to the suitability of each of these options, but the customer shouldn't have to know how the shop is implemented and make such decision unprompted. Even given a continuation to some low level routine, anything the caller does is likely to be too far removed to make it sensible to resume the continuation. Eiffel deals with this by brutally its Design By Contract magic bullet: Since all the code catching the exception knows about is its own implementation, the only sane option it has is to try performing the subtask in a different way. Here's how Eiffel's rescue/retry might work in perl6, where rescue is spelled CATCH. class Picker{ # An individual order picker can pick a book, but may unfortunately # die of lung cancer, or just fail to find the book method pick($book){ fail Err::LungCancer if .is_smoker(); ... fail Err::BookNotFound if ...; } } class PickingDept{ # The picking department employs pickers to pick books # If an employee dies of lung cancer while picking an order, # they are removed from the roster, and a different picker is chosen # If the department doesn't have enough free pickers, # it just throws a too busy exception # Due to the retry semantics, the department will keep choosing pickers # until one of them can pick the book, or they all die of lung cancer, # (or they're all busy doing something else) method pick($book){ my $picker=.find_free_picker(); $picker.pick($book); ... CATCH{ when Err::NoPickersAvailable { fail Err::DeptTooBusy; } when Err::LungCancer { .fire_picker($picker); # Harsh, but we won't find him again retry; # This re-runs the tried block (method) from the top } when Err::BookNotFound { fail Err::OutOfStock; } # Optimistic, maybe default { fail; } } } } class BookStore{ # The book store has multiple picking departments, so if one of them fails # to find a book, the others can be tried. If that fails, they could order # the book from the wholesaler and defer the order, but I'm too lazy method order($book){ for @.depts - $dept { try{ $dept.pick($book); return; CATCH{ 1; } # We'll just try the next one } } fail Err::BookNotFound; } ... } class Customer{ # The customer doesn't have any say in how bookshops are run, so all # they can do if their order is refused, all they can do is try another # shop, or give up and go home method buy_book($book){ $random_bookshop.order($book); CATCH{ fail Err::BookObviouslyDoesntExist; } } } -- Peter Haworth [EMAIL PROTECTED] Hestons' First Law: I qualify virtually everything I say.
Re: Exceptuations
On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote: On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. No! Not well said at all! The exception handler knows *EVERYTHING* because it knows what exception it caught: CATCH { when some_kind_of_error { $!.continue($appropriate_value_for_some_kind_of_error) } } -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me climbs a brick wall with his fingers: neeyah! pgpZ51c9ny7Ry.pgp Description: PGP signature
Re: Exceptuations
On 10/5/05, Yuval Kogman [EMAIL PROTECTED] wrote: On Wed, Oct 05, 2005 at 16:57:51 +0100, Peter Haworth wrote: On Mon, 26 Sep 2005 20:17:05 +0200, TSa wrote: Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it? The innocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of its innards. Well said. No! Not well said at all! The exception handler knows *EVERYTHING* because it knows what exception it caught: I don't think it was a how is this possible, but more of a what business does it have?. And as far as I gathered, they're saying pretty much what you've been saying, but in a different way. It's about the continuation boundary; that is, if you're outside a module, you have no say in how the module does its business. You can continue only at the module boundary, replacing a return value from its public interface. Of course, exactly how this public interface is declared is quite undefined. Luke
Re: Exceptuations
Luke Palmer wrote: Of course, exactly how this public interface is declared is quite undefined. Reading this thread, I find myself wondering how a resumable exception differs from a dynamically scropted function. Imagine this code: sub FileNotWriteable( Str $filename ) { die can't write file: $filename; } sub write_file (Str $filename) { FileNotWriteable($filename) unless -w $filename; ... } sub my_program { temp sub FileNotWriteable( Str $filename ) { return if chmod +w, $filename; OUTER::FileNotWriteable( $filename ); } ... write_file(foo.txt); ... } Ignoring syntactic sugar, what semantics does exception handling have that a dynamically scoped function does not? In the case of non-resumable exceptions, we see things like deferred handling -- the exception is passed as a property of an undef value. I assume such an exception cannot be resumed, so it does appear to me that there are fundamental differences between resumable things, and non-resumable, deferrable, exceptions. What is the benefit of unifying them under a common syntax (CATCH blocks)? Larry suggested that the exception mechanism might be a way of unifying errors and warnings; but perhaps the opposite is true. Perhaps what we see is a needed to generalize the distinction between warnigns and errors. Dave.
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
. 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 cresume/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 cwith/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
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
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 cresume/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
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
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
Re: Exceptuations
TSa [EMAIL PROTECTED] writes: BTW, I would call *intentional* exceptions terrorism. So that would be all exceptions then. They all get implemented somewhere, even the ones that get thrown by builtins. CATCH Exception { say Why do you hate freedom? } -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
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 datacode 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
Re: Exceptuations
HaloO Piers, you wrote: TSa [EMAIL PROTECTED] writes: BTW, I would call *intentional* exceptions terrorism. So that would be all exceptions then. They all get implemented somewhere, even the ones that get thrown by builtins. I knew that the statement would emotionalize. Sorry to all who don't like it an this list. But somehow I found it describes the impression on the handling side somewhat. And I thought it illustrates that exceptions shouldn't be considered just another tool. CATCH Exception { say Why do you hate freedom? } I don't. But the freedom of the individual ends where the community begins. -- $TSa.greeting := HaloO; # mind the echo!
Re: Exceptuations
On Fri, Sep 30, 2005 at 18:02:46 +0200, TSa wrote: I knew that the statement would emotionalize. Sorry to all who don't like it an this list. But somehow I found it describes the impression on the handling side somewhat. And I thought it illustrates that exceptions shouldn't be considered just another tool. I think you're taking it too seriously. I'm 99% sure Piers was joking. Regardless, exceptions *are* just another tool. They let you write safe code in fewer words and with less distraction. For example, Either you linearly serialize the entire tree of possible events: if (my $handle = open file) { # the handle is open if (my $other_file = open other :w) { for =$handle - $line { unless ($other_file.print($line)) { $*ERR.print(other could not be written to: $!); # disk might be full if (close $other_file) { if (close $handle) { exit 1; } else { ...; exit 1 } } else { ...; exit 1 } } exit 0; } } else { $*ERR.print(could not open other for writing: $!); if (close $handle) { exit 0; } else { $*ERR.print(could not close file: $!); # not logical, # since we don't write to file, but this is safer exit 1; } } } else { print $*ERR, could not open file: $!; exit 1; } or you could throw exceptions: use fatal; my $handle = open file; my $other_file = open other :w; for =$handle - $line { $other_file.print($line); } If you are going to avoid exceptions because they are too much something for your taste, then I think you are misusing a language that has support for exceptions. I really don't understand why this has to do with freedom, or it's restriction. It's your personal (and IMHO bad) taste not to use exceptions for improving your code, but it's still your choice. All I was saying is that you could leverage exceptions by letting the UI code make the handling of exceptions a two way route, instead of one way. CATCH Exception { say Why do you hate freedom? } I don't. But the freedom of the individual ends where the community begins. I think this is a big exaggeration. The community will not be harmed if the individual uses exceptions. On the contrary, i would be much happier to use code that does through exceptions. For example, a very useful perl 5 module, UNIVERSAL::require, lets me write: $class-require or die $UNIVERSAL::require::ERROR; instead of eval require $class; die $@ if $@; but in both cases I have to check for errors, unlike require Class; I still prefer $class-require, though, because it feels more readable to me. I don't say to myself wtf? why is this code doing an eval while reading the code. In perl 6, we would ideally have: use fatal; $class.require; # lives if class exists, dies if class doesn't exist $class.method; # always lives (if method really exists) or use fatal; try { $class.require } # always lives $class.method; # might die, but at least it's obvious or no fatal; $class.require; # always lives\ $class.method; # might die In fact UNIVERSAL::require's author agrees with me: http://use.perl.org/~schwern/journal/26939 Now, if this were #!/usr/bin/perl use fatal; use Pluginish::App; sub infix:s~ ($l, $r) { $l $r }; # spacey concatenator { Pluginish::App-load_plugins; CATCH { when Module::load_error { if (prompt(The module $!.module_name could not be loaded because s~ an error occurred ($!). Try to continue anyway?) { $!.resume(undef); } else { die $!; } } } Pluginish::App-run; -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /methinks long and hard, and runs away: neeyah!!! pgpKTxHNv1uR1.pgp Description: PGP signature
Re: Exceptuations
HaloO, Yuval Kogman wrote: On Wed, Sep 28, 2005 at 09:49:11 -0700, Larry Wall wrote: But thinking about optional continuations, another thing occured to me. It's always bugged me that warnings were something different from exceptions, and now I think we can unify them, if we say that Yes, I'm also all for unifying the concepts. But please don't let us call it exception. Exception should be a termination oriented (sub)concept. Some kind of scoped giving up. E.g. a traffic accident is the exceptual case that stops the regular traffic in a certain area. But for medics, firemen and police they are the usual case up to a limit where it also becomes exceptional to them. Take the example of firemen. They are some kind of installed exception handlers. And they can handle flooded basements but New Orleans and Bavaria have thrown a too big exception. Well, or take the engineers of the Titanic, they trusted their installed exception handlers to cope with a breached hull so much that they didn't install enough rescue boats. BTW, I would call *intentional* exceptions terrorism. a warning is simply an exception with two properties. or have a WARN block of its own, I don't know. Or maybe *EXCEPTION_HANDLER is a multi-method-continuation. ... An MMD exception handler that is extended in the dynamic scope is cool because it's not limitied to just control exceptions, warnings, and fatal errors. Some fun definitions: In lack of a better word I use Event and we get Event::Exception, Event::Control, Event::Warn and possibly Event::Hint, Event::Log and Event::Fun :) The only drawback of choosing 'Event' might be that it smells too GUIish for some? Or is that a feature? That is we get Event::GUI::press, Event::GUI::scroll, Event::GUI::expose, etc. -- $TSa.greeting := HaloO; # mind the echo!
Re: Exceptuations
Hi! On Thu, Sep 29, 2005 at 10:49:40 +0200, TSa wrote: BTW, I would call *intentional* exceptions terrorism. Then I would call terrorism non linear control flow ;-) In that case Afghanistan might be harboring computer scientists that really like CPS, and bush is Java ;-) In lack of a better word I use Event and we get Event::Exception, Event::Control, Event::Warn and possibly Event::Hint, Event::Log and Event::Fun :) An event is something external that happens... It does not affect the control flow of an application. Exceptions work well to describe this: The normal flow is up and down the call graph, laying bread crumbs in the form of a stack, and going back from where you came. Exceptions let you jump instantly to another place in the call graph. All I'm saying is that to complement the normal exceptions jump back out, you can jump out, and jump back in. An event is a general thing that can be implemented with exception like semantics, but implies nothing on the control flow of the program. An error is an exception that happenned because something went badly wrong. Now it's my turn at illustrative metaphors ;-) The call graph is a big maze you go through. When you find an *error*, in the form of a minotaur, you use the exception handling mechanism to run away. You jump instantly to a safe place in the labyrinth. Although this is not consistent with the mythology, presume that the reason you entered the maze was that you were trying to get results. Most exception handlers are safe places when you can gather your self, realize that 'oh crap, i just met a minotaur', and tell whoever you sent you in there that there's no way you're going back in. All I'm trying to say is that when there is an exception - a leap out from a dangerous place to a safe one, due to an error, the code may choose to deal with the error by giving you a big stick. Then you can go back and beat the minotaur into submission, and resume with trying to get results. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me whallops greyface with a fnord: neeyah!!! pgp9mjhsoAPUd.pgp Description: PGP signature
Re: Exceptuations
I'd like to ammend, and perhaps formalize with some definitions from my dictionary, which ships with OSX: error - a mistake... the state or condition of being wrong in conduct or judgement... technical - a measure of the estimated difference between the observed or calculated value of a quantity and it's true value. As i see it: when something you wanted to happen turned out different than you programmed it to: my $handle = open file; # we assume $handle exists print =$handle; # but what if there was an error? exception - a person or thing that is excluded from a general statement or does not follow a rule To lessen the mental load on a programmer, instead of having the programmer write a tree of all the conditions that could happen, the programmer can write only the condition in which the program is actually useful. Any *error* (synch problems between the code and reality) causes an *exception* in this linearization. The control flow is an exception to the norm, because there was an exception in the reality the program was tailored to deal with. The reason we handle exceptions is that sometimes we want the tree approach, because we have well defined behavior for certain paths. Exceptions let us separate code from normal code and code which is *exceptional*, and the reason it is exceptional is usually an error. event - a thing that happens, especially one of importance Every error is an event. Exceptions are one way to deal with events we were not prepared for. But events can also be waited for (select waits for events on many file handles at a time). Every *error* is an *event*, but an *exception* is how you deal with events. The events that cause exceptions by default in perl will be: errors, when 'use fatal' is in effect warnings, but to another handler next, redo, last, return - all control flow events that are exceptions to the single point of entry, single point of exit school of thought. I intentionally did not use the computer related definitions from e.g. wikipedia, because they are more subject to cultural inertia, and we are trying to discover the roots of these borrowed terms. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me groks YAML like the grasshopper: neeyah!! pgpr89lYnXBsX.pgp Description: PGP signature
Re: Exceptuations
TSa schreef: Yes, I'm also all for unifying the concepts. But please don't let us call it exception. Exception should be a termination oriented (sub)concept. Some kind of scoped giving up. [...] In lack of a better word I use Event and we get Event::Exception, Event::Control, Event::Warn and possibly Event::Hint, Event::Log and Event::Fun :) The only drawback of choosing 'Event' might be that it smells too GUIish for some? Or is that a feature? That is we get Event::GUI::press, Event::GUI::scroll, Event::GUI::expose, etc. FSM etc. http://en.wikipedia.org/wiki/Event_driven_finite_state_machine Running Perl code can be seen as an ATN (an FSM on steroids, says http://smc.sourceforge.net/SmcFaq.htm). An exception-handler can be allowed to pop more states than it is good for. -- Grtz, Ruud
Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
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 csub Dog foo()/c but there may be times when I discover, after making the call, that I really needed to call an anonymous csub { $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 cuse 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 cthrow/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 wants coming up.] So cresume/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
Re: Exceptuations, fatality, resumption, locality, and the with keyword; was Re: use fatal err fail
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 csub Dog foo()/c but there may be times when I discover, after making the call, that I really needed to call an anonymous csub { $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
Re: Exceptuations
I'd like to add a little more. The context of the original throw probably knows best whether it's even possible or sensical to continue, so it should be optional whether the exception contains a resume continuation. The presence of the continuation signals that the inner code would *like* to continue but can't figure out how without more dynamic context, where that could even include human interaction on some level. The absence of a resume continuation means someone thinks this dynamic branch needs a wooden stake through its heart. But thinking about optional continuations, another thing occured to me. It's always bugged me that warnings were something different from exceptions, and now I think we can unify them, if we say that a warning is simply an exception with two properties. The first is that it's in a category that, by default, the outermost runtime will just report on and resume. And the second is, of course, that the resume continuation is required. So turning any warning into a fatal error consists of stripping the resume continuation. (Which might mean that the presence of the continuation is what differentiates warnings from fatal errors, but I doubt it. There needs to be a class of exceptions that have a resume continuation that is not resumed by default. A die should throw that kind of exception, and have a reasonably expectation of not being resumed in the normal course of things. Nevertheless, a die is well formed enough that we know where it should resume if it forced into a warning instead.) This gives us the ability to capture any warning in the dynamic scope and do something about it. (Whether it should by default pop up in a CATCH block or a CONTROL block, or have a WARN block of its own, I don't know.) But this gives us dynamic control over warnings to complement (not replace) the lexical control provided by use warnings. Larry
Re: Exceptuations
On Wed, Sep 28, 2005 at 09:49:11 -0700, Larry Wall wrote: I'd like to add a little more. The context of the original throw probably knows best whether it's even possible or sensical to continue, so it should be optional whether the exception contains a resume continuation. The presence of the continuation signals that the inner code would *like* to continue but can't figure out how without more dynamic context, where that could even include human interaction on some level. The absence of a resume continuation means someone thinks this dynamic branch needs a wooden stake through its heart. But thinking about optional continuations, another thing occured to me. It's always bugged me that warnings were something different from exceptions, and now I think we can unify them, if we say that a warning is simply an exception with two properties. The first is that it's in a category that, by default, the outermost runtime will just report on and resume. And the second is, of course, that the resume continuation is required. So turning any warning into a fatal error consists of stripping the resume continuation. (Which might mean that the presence of the continuation is what differentiates warnings from fatal errors, but I doubt it. There needs to be a class of exceptions that have a resume continuation that is not resumed by default. A die should throw that kind of exception, and have a reasonably expectation of not being resumed in the normal course of things. So CATCH does set the *EXCEPTION_HANDLER continuation to be something like: given $! { when Exception::ResumesByDefault { old_exception_handler($!); } ... # the body of the CATCH } and or have a WARN block of its own, I don't know. Or maybe *EXCEPTION_HANDLER is a multi-method-continuation. CATCH could simply be a macro that compiles to ENTER { temp *EXCEPTION_HANDLER := *OUTER::EXCEPTION_HANDLER; EXCEPTION_HANDLER.add_variant(cont (Exception::Fatal $!) { }; } BTW, how do we declare continuations explicitly? Or is it just a sub that is directly .goto'd ? An MMD exception handler that is extended in the dynamic scope is cool because it's not limitied to just control exceptions, warnings, and fatal errors. Some fun definitions: every loop construct creates some lexical bindings in the body: next - throws an Exception::Control::Next redo - throws an Exception::Control::Redo ... Entering the loop adds temporary variants to *EXCEPTION_HANDLER: multi cont (Exception::Control::Redo) { body.goto(*$current_binding_tuple); } multi cont (Exception::Control::Next) { ... } -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me climbs a brick wall with his fingers: neeyah! pgpiYc6duPVGk.pgp Description: PGP signature
Re: Exceptuations
And handling user errors in a GUI application is a task for event handling, *not* exception handling. I agree that both mechanisms share large parts of the infra-structure supporting them. But I make a strong conceptual distinction between them. Which leads to the question, does Perl6 have or need a build-in event system on the language level? -- Hear, hear. Isn't just an exception a naughty event? But when you think about it, isn't all programming about suggesting events and handling what happens next? Most of what a programmer does is suggest events to do (e.g., open file) and then handle the 'events' that happen next (e.g., file not found) and then suggest some more events (e.g., die). At the moment the programmer is the top-level event handler (e.g., damn - why didn't that work?) - but maybe some of this hassle could be handed off? Is there a way to turn the exceptions type matching / depth finding problem inside out? From the point of view of the operating system a program is a nasty exception to its normal running environment - your whole program is a kind of big exception! Like someone intruding on a conversation the programmer tries to change the topic of the operating system. Hey can you get that network socket for me? Programmers are constantly trying to change the topic of conversation of the operating system. Hey drop what you're doing. This is important. Get me that file. But some of the nastiest (and exception raising) problems arise from the conversational disconnect between operating system and program (e.g., Sorry that port is blocked, Sorry can't find that file, Socket closed etc). Is there a place in the room for a third party to the conversation? Somebody who could ... Smooth over misunderstandings? Do translation if either party doesn't understand? Queue what should be the next topic of conversation? Remember how topics are meant to be handled? Provide a log of what was said? Is there a way of integrating Perl's natural ability to stay on topic ($_) with exception handling, and event handling? Just some thoughts ... NIge
Re: Exceptuations
On Tue, Sep 27, 2005 at 07:29:48 +0100, Nigel Hamilton wrote: And handling user errors in a GUI application is a task for event handling, *not* exception handling. I agree that both mechanisms share large parts of the infra-structure supporting them. But I make a strong conceptual distinction between them. Which leads to the question, does Perl6 have or need a build-in event system on the language level? The exception mechanism is an event system that is typically used for nothing but errors, but is much more extensible than that. In fact, exceptions are nothing more than continuations really. The CATCH block 'temp' sets a global exception handler continuation, and die is simply sub die (*$a) { *EXCEPTION_HANDLER.(*$@) } Also, it's hard for a library writer to consistently decide what is an error and what isn't. It's also too much trouble. If every library had severity levels, things would be complicated for small scripts. On the other hand, whenever an event that is not what 90% of the flow control should be does occur, exceptions let you delegate the behavior of how to deal with this to other code, specifically the code that called you. It just happens that the default *EXCEPTION_HANDLER continuation is essentially print STDERR @_; exit $bad_error_code; Hear, hear. Isn't just an exception a naughty event? But when you think about it, isn't all programming about suggesting events and handling what happens next? Well, not necessarily. It's an event that is exceptional, i.e. not normal. 99% of non normal events are errors, but we routinely use exceptions for timeout, etc. { temp $SIG{ALRM} = { die timeout; } # we need a better mechanism for this in perl 6, IMHO alarm 10; connect(...); CATCH { when timeout { $!.resume if prompt(The remote party does not seem to be responding. Keep trying?); } default { $!.rethrow } } } Timing out is not an error unless the user decides it is. Normally the user would have to decide beforehand when lack of responsiveness becomes an error, but with a system like this the user can be prompted on a case by case basis to decide whether it's an error or not. Is there a way of integrating Perl's natural ability to stay on topic ($_) with exception handling, and event handling? As I see it continuations are a way to get back on topic... Hey, 3rd party, the underlying thingy told me that yadda yadda... If you can fix it, here's what you do next. This is why I proposed the idea. I would like to expand on your ideas though - the program is layered with a delegation hierarchy... The lowest level agent is the operating system. On the other side, the user makes a query to the program which makes a query to it's first level handler, that uses a library, that uses another and so on and so forth. In a sense, the third party you mention is always the caller of a given block of code, with the other two parties being the called level, and the level below it. Intervention on behalf of the third party can be delegated upwards if this layer does not have enough policy on event handling. The only level which really knows how to behave under any event (hopefully ;-) is the user. Just some thoughts ... Good post! -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me does a karate-chop-flip: neeyah!! pgpFk63eGjRwQ.pgp Description: PGP signature
Re: Exceptuations
Nigel Hamilton schreef: From the point of view of the operating system a program is a nasty exception to its normal running environment - your whole program is a kind of big exception! As if a real OS really likes to run idle most of the time. ;) Like someone intruding on a conversation the programmer tries to change the topic of the operating system. Hey can you get that network socket for me? Programmers are constantly trying to change the topic of conversation of the operating system. Hey drop what you're doing. This is important. Get me that file. Or the OS is more like a waiter, nervously circling the tables, watching out for orders. Is there a way of integrating Perl's natural ability to stay on topic ($_) with exception handling, and event handling? http://poe.perl.org/?POE_FAQ/Will_Perl_6_make_POE_obsolete And how about agents and messages? -- Grtz, Ruud
Re: Exceptuations
Luke Palmer [EMAIL PROTECTED] writes: On 9/25/05, Yuval Kogman [EMAIL PROTECTED] wrote: I propose a new model - each exception has a continuation that allows it to be unfatalized. I think we've already talked about something like this. But in the presence of use fatal, it makes a lot more sense. Something comes to mind: use fatal; sub foo() { bar() } sub bar() { baz() } sub baz() { quux() } sub quux() { fail } { say foo(); CATCH { $!.continue(42) } } Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Assuming caller returns a continuation (which I still fondly hope it will). I'm assuming you're examples aren't necessarily tail calls of course. Where do we cut off the call chain and replace our own value? This comes up again with open(). Let's say open is implemented with a series of five nested calls, the innermost which knows how to fail and propagate outwards. However, the programmer using open() has no idea of its internals, so it ought to override the return value of open() itself, rather than its utility functions. However, we can't go with outermost, because then you'd only be fixing the lexical call (say foo() above). So it's somewhere in between. Where? Obviously the topmost function should do: sub open(...) { ... CATCH { $!.rethrow } } This assumes that 'rethrow' throws a new exception that delegates to the original exception for lots of its behaviour. If, later, you want to explicitly get at the exception thrown by the helper function, you could do something like: $!.inner -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Exceptuations
HaloO, Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it. The inocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of it innards. Think of receiving a 'shelf picker died of lung cancer' exception when you just ordered a book from your favorite book dealer. Irrespective of the seriousness to the shelf picker, but how would you expect a customer to handle such an exception? To me exceptions are to be handled with respect to the service the scope invoked. In my example I would conclude that the book dealer has more than one shelf picker under contract and just re-issue the order. Well, or buy it elsewhere. In other words if the task at hand is buying a certain book, exception handling means iterating all viable sources with the assumption that the most convenient one *usually* delivers. Otherwise I would code an iteration over the sources right away. And handling user errors in a GUI application is a task for event handling, *not* exception handling. I agree that both mechanisms share large parts of the infra-structure supporting them. But I make a strong conceptual distinction between them. Which leads to the question, does Perl6 have or need a build-in event system on the language level? -- $TSa.greeting := HaloO; # mind the echo!
Re: Exceptuations
TSa [EMAIL PROTECTED] writes: HaloO, Piers Cawley wrote: Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) Whow, how does a higher level exception catcher *in general* know what type it should return and how to construct it. It asks the continuation? The information should be there. Consider a function definition: sub whatever(...) returns List { throw ResumableException; } Then, assuming that caller returns continuation -- which is a pretty big assumption, but which would be really cool -- $!.caller(1) would be a continuation with an signature of (List $not_a_real_name). If the function can return several types dependent on context, then the continuation's signature would be the appropriate one for the context in which the function was called. Monkeying with this kind of thing in code isn't necessarily a good idea, but it's great for, for instance, having a top level exception catcher that could (potentially) bring up an interactive shell with limited debugging features which would allow you to inspect the running program and work out what went wrong. Ruby on Rails already does something like this, with the added wrinkle that, if it hits a breakpoint when running in the server it hands off the interactive session to a console process rather than having you monkey with it within your browser. A very neat trick and a remarkably powerful debugging technique. The inocent foo() caller shouldn't bother about a quux() somewhere down the line of command. Much less of it innards. Think of receiving a 'shelf picker died of lung cancer' exception when you just ordered a book from your favorite book dealer. Irrespective of the seriousness to the shelf picker, but how would you expect a customer to handle such an exception? I wouldn't, but I would expect that a programmer would find such an exception very useful indeed. -- Piers Cawley [EMAIL PROTECTED] http://www.bofh.org.uk/
Re: Exceptuations
On Mon, Sep 26, 2005 at 17:40:52 +0100, Piers Cawley wrote: Luke Palmer [EMAIL PROTECTED] writes: On 9/25/05, Yuval Kogman [EMAIL PROTECTED] wrote: I propose a new model - each exception has a continuation that allows it to be unfatalized. I think we've already talked about something like this. But in the presence of use fatal, it makes a lot more sense. Something comes to mind: use fatal; sub foo() { bar() } sub bar() { baz() } sub baz() { quux() } sub quux() { fail } { say foo(); CATCH { $!.continue(42) } } Exactly which exception is continued? The bottommost one. If you want to return to somewhere up its call chain, do: $!.caller(n).continue(42) This breaks encapsulation, like luqui mentioned. However, since every exception has an exception stack, i guess you could see exactly how it was propagated non-fatally, before it was actually thrown. sub open(...) { ... CATCH { $!.rethrow } } ... $!.inner That way behavior like that could be automated -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me sushi-spin-kicks : neeyah pgpz89jwQwGFi.pgp Description: PGP signature
Exceptuations
Hi, Suppose I'm writing a file browser, with a pane on the left to display the filesystem hierarchy, and a frame on the right to preview the file. Suppose I have a convenience function, preview_file which takes a path and returns a value that the frame display view knows to render. Let's define this for HTML files, where the desired preview is a summary of the text: use Text::Summarize; use HTML::ToAscii; multi sub preview_file ($filename where /\.html$/ ) { my $handle = open($filename, :r); # might fail if $filename is unreadable return summarize(html2ascii(=$handle)); # might fail if HTML is invalid } And this code is called when the user clicks on a file in the pane: class NiftyUI { use fatal; method handl_some_click ($file) { $.right_frame.display(preview_file($file.name)); } method handle_event ($event) { $?SELF.dispatch_event($event); CATCH { when NiftyBackend::Exception { $?SELF.display_error_box($!); } default { die $! }; } } } With the current shape of the code if any of the possible failures in the backend code happen, they are reported in a message dialog. Now, let's say we would like to add a feature, that lets the user change the mode of the file if it's unreadable. Several approaches to doing this: * give the backend an abstract object, a Frontend of sorts: $frontend.ask_user(do you want to make the file readable?) * throw internal exceptions, and let the frontend handle the exception and retry the action: method handle_some_click ($file) { $.right_frame.display(preview_file($file.name)); CATCH { when IO::Errors::PERMISSION_DENIED { if ($?SELF.ask_user_to_chmod_file($file)) { make_readable($file); $?SELF.handle_some_click($file); # retry the event } else { die $! } } } } I propose a new model - each exception has a continuation that allows it to be unfatalized. The exception object has info on whether the fatality of the exception was due to a die, or a use fatal, and lets the exception handler decide accordingly. Then we have code that looks like this: method handle_some_click ($file) { $.right_frame.display(preview_file($file.name)); CATCH { when IO::Errors::PERMISSION_DENIED { if ($?SELF.ask_user_to_chmod_file($file)) { make_readable($file); $!.continue(open($file, :r)); # the return value of the failed open is # overridden by the return value of this (hopefully successful) open. } else { die $! } } when HTML::ToAscii::Exception { # badly formed $!.continue(The HTML file contained a syntax error); # this string is passed to summarize instead of the undef exception object } } } The value this has is in handler cascading... If, for example, make_readdable($file) failed too, then an exception handler around handle_some_click could handle display a sudo box, to try it again, and eventually continue back into the make_readable($file) call, which will then continue into the failed open. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me tips over a cow: neeyah!! pgprazW7gmwVh.pgp Description: PGP signature
Re: Exceptuations
In order to support continuable exception generators, here is a style guide for exception values, and an observation on what exceptions should support at the language level: * Exceptions should preserve data All exceptions should contain as much of the original data that caused them... With Luke's new theory proposal, storing the argument tuple for the call the raised the exception is logical. For example, for a Permission Denied error raised in open() we should have access to the filename that we tried to open, and the mode we tried to open it with. * Each thrown exception should contain the value of $! from before it was thrown * Each thrown exception has to know whether it was thrown because of 'use fatal' or because of an explicit 'die' * fail should be used except for very strict conditions * use fatal should be encouraged by the documentation, for one liners, scripts, and full blown apps. * Context data should be overridable at the exception object level Carp and friends should be implemented not by using a different keyword than fail, but failing with a value that specifies a certain context. sub croak (*$a) { # tuples again fail Exception.new(*$a, :position($?CALLER::CALLER::POSITION)); } * fail should cascade such that every time a block is left with a failing return value, the block's caller should be checked for use fatal. * When continuing into an exception, the parameters to the continuation are substituted for the error. use fatal; my $value = { fail foo; CATCH { $!.continue(bar); } } say $bar; Continuing into the exception with no arguments is like ignoring 'use fatal' for this specific exception, causing the failed value to propagate out of the fatalized block. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me wields bonsai kittens: neeyah pgpE7Wb5OOFbg.pgp Description: PGP signature
Re: Exceptuations
On Sun, Sep 25, 2005 at 18:11:22 +0300, Yuval Kogman wrote: In order to support continuable exception generators, here is a style guide for exception values, and an observation on what exceptions should support at the language level: And more... * Exception strings are for humans Humans need to know what happened in their own words. They have a hard time analyzing error codes, or constant names. Computers have a hard time analyzing strings, and prefer hard data. It should be easy to identify each error in a way that is meaningful to computers, and this way should be orthogonal to the string that is displayed to the user when possible. This means that it should be incredibly easy for the user to declare exception classes as they are used, perhaps by stealing goodness from enumish constructs, or somesuch. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: *shu*rik*en*sh*u*rik*en*s*hur*i*ke*n*: neeyah pgpcpkWrPtIwr.pgp Description: PGP signature
Re: Exceptuations - CPS explained
To ease the understanding of exactly how this might work, assume that perl 6 is really all continuation passing style under the surface (parrot is ;-). use fatal; my $x = do_bar(); do_foo(); sub do_bar { fail bah; } The way CPS works is really simple. The principal is that there is no concept of return. Instead, the call to do_bar, for example, gets an additional implicit parameter. This parameter is the code ref to the rest of the code, and could look something like this if deparsed: sub ($rv) { my $x = $rv; do_foo() } So do_bar knows to make a goto to that code ref with it's return value. Now, fail gets something similar - it gets the code ref which applies do_bar's passed in code ref, and it applies it the exception constructed from bah. Fail will also check first to see if the continuation might, at some level, use fatal. If it does, it takes a dynamically scoped value instead - the continuation for the last encountered CATCH block. When it throws the exception, fail will store the continuation it got (the $x = ... stuff) in the exception object. CATCH can then choose either to goto it's parent block's continuation (exit normally), or to goto the continuation stored in the exception object, which is indirectly the assignment to $x. Autrijus's example makes this clear (he prefers resume over continue): use fatal; my $x = { 1/0 * 3 CATCH { $!.resume(9) } }; # 27 What's happening is that infix:/ checks if it's divisor is 0, and if it is, it will fail. Fail has a continuation to 'multiply by 3 - exit from block - assign to $x', and it stores that in the exception object. Instead of gotoing to that continuation, it will instead take the last CATCH block that was relevant, in this case the only one. This one finds the continuation to * 3 etc in the exception object, and effectively augments the result of infix:/(1, 0) to be 9 instead of an undefined value. I hope this makes things clear. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me does not drink tibetian laxative tea: neeyah! pgpC58pLTHBic.pgp Description: PGP signature
Re: Exceptuations
On 9/25/05, Yuval Kogman [EMAIL PROTECTED] wrote: I propose a new model - each exception has a continuation that allows it to be unfatalized. I think we've already talked about something like this. But in the presence of use fatal, it makes a lot more sense. Something comes to mind: use fatal; sub foo() { bar() } sub bar() { baz() } sub baz() { quux() } sub quux() { fail } { say foo(); CATCH { $!.continue(42) } } Exactly which exception is continued? Where do we cut off the call chain and replace our own value? This comes up again with open(). Let's say open is implemented with a series of five nested calls, the innermost which knows how to fail and propagate outwards. However, the programmer using open() has no idea of its internals, so it ought to override the return value of open() itself, rather than its utility functions. However, we can't go with outermost, because then you'd only be fixing the lexical call (say foo() above). So it's somewhere in between. Where? Luke
Re: Exceptuations
On 9/25/05, Luke Palmer [EMAIL PROTECTED] wrote: [...] Exactly which exception is continued? [...] Named restarts in Common Lisp appear to try to solve a related problem, if I'm skimming this thread correctly. :-) (see [1]). Michael [1] http://www.supelec.fr/docs/cltl/clm/node312.html#SECTION00330
Re: Exceptuations
On Sun, Sep 25, 2005 at 11:32:54 -0600, Luke Palmer wrote: Exactly which exception is continued? ... This comes up again with open(). So it's somewhere in between. Where? For the open() example I don't have a solution but I know in what direction to throw it: the same thing that makes croak appear to work from the caller =) Several things come to mind... First is this: (ugly) sub open { ... CATCH { fail $! }; # force it to go up } This is the most flexible, but also the crudest method. Another idea is to use the same namespace hopping logic that Carp does right now. This doesn't work for exceptions in the same class so it should be optional and off by default... Perhaps: fail error :package_boundry; # better word needed Another idea is to use a lexical pragme that delays all errors, which is like CATCH { fail $! } but more declarative: sub open { use fatal propagate; ... } For when it isn't specified, when doing evil things such as stepping into the continuation of an exception, we assume that the code doing the CATCH knows about the code that raised the exception at least something. Encapsulation is sort of maintained by specifying what kinds of operations are fixed.. For example, errors having to do with opening files are fixuppable by taking the arguments to open found in the exception, and trying to reopen the file after some fudging, and returning a handle. This is common knowlege about the interface of open, and we are simply saying that for the rest of the dynamic scope (dangerous, perhaps) we are trying to fixup every call to open that fails with this handler. This is no different than catching that exception in terms of encapsulation, except that the failure is defatalized. My claim is that just as you know the kind of error it is when you explicitly catch it for the purpose of reporting, you have the same knowlege when you are fixing. -- () Yuval Kogman [EMAIL PROTECTED] 0xEBD27418 perl hacker /\ kung foo master: /me sushi-spin-kicks : neeyah pgpCOTXugoPT2.pgp Description: PGP signature