Re: Catching exceptions with the // operator
Thank you very much! my $bill = try ack() orelse try thpp() orelse do ppt(); This certainly does what I asked for, and it's short enough (even if we need to add a few brackets). Yes, the basic problem with the proposal is that it catches all exceptions willy nilly and uses them all for control flow whether that is appropriate or not. I have some sympathy with the proposal, however, because I'm currently using exceptions for just that purpose in the STD grammar. But it's suffering from the same difficulty, and I have to be careful to rethrow if the exception I trapped wasn't the one I expected. ... It might also be useful to have a try variant that only catches exceptions intended as benign failure to match. Or maybe try should have a way of indicating the severity of exception it wishes to trap by default. This could be a keyword variant, or an adverb, or a pragma. I'm thinking the pragma is most likely to distribute syntactic relief nicely over a lexical scope. In any case it would only set the default CATCH handler which could still be overridden in any particular try block. ... Larry I believe that ---from a usability point of view--- it's very important to: * classify exceptions by severity or other characteristics, * provide named adverbs/pragmas to modify default CATCH handlers, * make them configurable by outer scopes. Without such conventions (e.g. with just catching individually named exception classes), the programmer of a function often cannot reasonably know what exceptions he should catch and how he should handle them. There are two reasons for this and here are two examples. The first reason is: The programmer isn't supposed to know all exceptions that can be thrown by sub-routines. Otherwise, you can't add a new feature to a library function (and introduce a new exception with it) without breaking the error handling of all code that uses this library function. Also, some general functions (e.g. macro/template-expanders) can include an unspecified set of extension modules and hence can't know all the exceptions that will be needed for these extensions. This problem can be solved by grouping exceptions into meaningful groups and catching groups, not individual exceptions. For example, there are quite a lot of situations where a function cannot produce an expected result (e.g. read_file($non_existing_file)). Such exceptions just say that you can't rely on any results that depend on the output of this function. These exceptions should be catched by //. A different group of exceptions is where the user wants to terminate a computation (e.g. CTRL-C//HUP). The second problem goes into the opposite direction: Even if you have well-defined exception groups, you cannot always know how they should be handled. Take, for example, a macro processor (like LaTeX) used for typesetting a book and an exception that indicates that some required information (e.g. a reference) is not available. Should this exception be catched and a placeholder be included --- or should this exception be fatal for this job? The answer is: during proofreading, this should produce a warning and a readable result. In production scripts (process and print a few million books), this needs to be fatal. Of course, the programmer of the macro processor cannot decide whether the processor is used in proofreading or in production code. This decision must be made upstream, by callers of the processor. The same problem is: should attempts to access protected files be fatal security alerts or just missing values? This problem can be resolved by having configurable CATCH handlers that are defined by the callers of a function and only referred to by name. So, we may have three separate programmers that are responsible for separate decisions: The programmer of a low-level function or extension module throws exceptions and classifies them into meaningful severity groups. The programmer of the top-level function defines the severity of particular exception groups for this specific application by defining named CATCH handlers. The middle-ware programmer doesn't have the responsibilities of the low-level or top-level programmers, and can't make decisions for them. He just uses the appropriate named CATCH handler to do the right thing. And if we really go so far, we can have standard names for two CATCH handlers: CATCH:// and CATCH:orelse. The operators can catch the corresponding exceptions --- leaving the specification of which exceptions to handle to the programmer.
Re: Catching exceptions with the // operator
HaloO, Yaakov Belch wrote: I believe that ---from a usability point of view--- it's very important to: * classify exceptions by severity or other characteristics, * provide named adverbs/pragmas to modify default CATCH handlers, * make them configurable by outer scopes. [..] The programmer of the top-level function defines the severity of particular exception groups for this specific application by defining named CATCH handlers. I want to propose---possibly again ;)---to use the dispatch semantics for this purpose. The idea is that CATCH blocks can be made into multis first of all with the multi keyword or with a proto which then makes inner CATCH blocks also multi unless they are explicitly declared only. Generally speaking I find multiple CATCH blocks with a signature better than a big switch inside a single one. Such a CATCH without signature would be good for low-level handling, though. It has implicit signature :(Any) and is easily overridden from outer scopes. The grouping comes naturally since packages are recognized as type names. So we can have proto CATCH {...} # force multi { multi CATCH (Math::DivZero) {...} { multi CATCH (Math) {...} multi CATCH (Math::Overflow) {...} # more specific # outer catch seen here as well { CATCH { when Math {...} # never called } } } } And if we really go so far, we can have standard names for two CATCH handlers: CATCH:// and CATCH:orelse. So with CATCH a magic namespace these would be CATCH::// and CATCH::orelse respectively. Using the namespace is another approach. But to me it doesn't feel as natural as the exception type approach. Regards, TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Catching exceptions with the // operator
In a little language that I wrote some time ago, I found it very useful to let the // operator catch exceptions: f(x) // g(y) does: * If f(x) returns a defined value, use this value. * If f(x) returns an undefined value, use the value of g(x) instead. * If f(x) throws an exception, catch and keep it in $!, use the value of g(x) * But don't catch exceptions of g(x). Similarly, f(x) // g(y) // h(z) catches exceptions of f(x) and of g(y) but not of h(z). I would like to suggest the same semantics for perl6. Let me explain why this is useful and why I think this is the right thing: First of all, it provides a very light-weight exception handling using well-known ideoms like: $file_content=read_file($filename) // $default_value; compute_statistics($data) // write_log_message(stats failed: $!); With the proposed change, these ideoms work whether the functions throw exceptions or not. But why should this be the right thing? Obviously, // is the fallback or redundancy operator: Don't despair if the first computation doesn't produce a usable result --- we have another way of getting the job done. In this context, and exception conveys the same message as an undefined value: The first step failed. You need to fall back to some other alternative or give up! As the second expression provides exactly this other alternative, there is no need to jump out of the normal processing order anymore. Best regards --- and many thanks for the continued effort on perl6! Yaakov Belch
Re: Catching exceptions with the // operator
On Wed, Aug 6, 2008 at 8:58 AM, Yaakov Belch [EMAIL PROTECTED] wrote: In a little language that I wrote some time ago, I found it very useful to let the // operator catch exceptions: f(x) // g(y) does: * If f(x) returns a defined value, use this value. * If f(x) returns an undefined value, use the value of g(x) instead. * If f(x) throws an exception, catch and keep it in $!, use the value of g(x) * But don't catch exceptions of g(x). Similarly, f(x) // g(y) // h(z) catches exceptions of f(x) and of g(y) but not of h(z). I would like to suggest the same semantics for perl6. Let me explain why this is useful and why I think this is the right thing: First of all, it provides a very light-weight exception handling using well-known ideoms like: $file_content=read_file($filename) // $default_value; compute_statistics($data) // write_log_message(stats failed: $!); With the proposed change, these ideoms work whether the functions throw exceptions or not. But why should this be the right thing? Obviously, // is the fallback or redundancy operator: Don't despair if the first computation doesn't produce a usable result --- we have another way of getting the job done. In this context, and exception conveys the same message as an undefined value: The first step failed. You need to fall back to some other alternative or give up! As the second expression provides exactly this other alternative, there is no need to jump out of the normal processing order anymore. i don't think this will work for perl 6. since perl 6 has resumeable exceptions (like Cwarn), the meaning of the C// operator could be ambiguous. given the following statement, my $bill = ack() // thpp() // ppt(); with perl 6's current semantics, if Cack(), throws a resumable exception that is handled in the current scope or an outer scope, execution will resume before C// and the definedness of the result from Cack() will be tested in order to determine whether or not to call Cthpp(). using your semantics, if a resumable exception is thrown in Cack(), C// will cause Cthpp() to be invoked immediately, discarding any possible defined result from Cack(). also, the question arises that if Cthpp() doesn't handle the type of exception thrown, should Cppt() be called immediately, or only if Cthpp() returns an undefined result? seems to me it would try to handle the exception thrown by Cack(). so how do i signify that my exception has been handled, and that i can now assign a default value to C$bill? in my mind, this strays too far from the meaning of C// and adds ambiguity that makes the operator unusable. perhaps there's room for an operator that gives some sugar for my $bill = try { ack() CATCH { thpp() } }; but to me that code is concise enough that it doesn't warrant syntactic relief. ~jerry
Re: Catching exceptions with the // operator
in my mind, this strays too far from the meaning of C// and adds ambiguity that makes the operator unusable. perhaps there's room for an operator that gives some sugar for my $bill = try { ack() CATCH { thpp() } }; but to me that code is concise enough that it doesn't warrant syntactic It seems that the following should address the issue while providing enough indication about what is occurring: my $bill = try { ack() } // thpp(); That seems to be closer to what the original post was desiring. Paul
Re: Catching exceptions with the // operator
On Wed, Aug 06, 2008 at 09:36:16AM -0700, jerry gay wrote: : i don't think this will work for perl 6. since perl 6 has resumeable : exceptions (like Cwarn), the meaning of the C// operator could be : ambiguous. given the following statement, : : my $bill = ack() // thpp() // ppt(); : : with perl 6's current semantics, if Cack(), throws a resumable : exception that is handled in the current scope or an outer scope, : execution will resume before C// and the definedness of the result : from Cack() will be tested in order to determine whether or not to : call Cthpp(). Yes, the basic problem with the proposal is that it catches all exceptions willy nilly and uses them all for control flow whether that is appropriate or not. I have some sympathy with the proposal, however, because I'm currently using exceptions for just that purpose in the STD grammar. But it's suffering from the same difficulty, and I have to be careful to rethrow if the exception I trapped wasn't the one I expected. : using your semantics, if a resumable exception is thrown in Cack(), : C// will cause Cthpp() to be invoked immediately, discarding any : possible defined result from Cack(). also, the question arises that : if Cthpp() doesn't handle the type of exception thrown, should : Cppt() be called immediately, or only if Cthpp() returns an : undefined result? seems to me it would try to handle the exception : thrown by Cack(). so how do i signify that my exception has been : handled, and that i can now assign a default value to C$bill? : : in my mind, this strays too far from the meaning of C// and adds : ambiguity that makes the operator unusable. perhaps there's room for : an operator that gives some sugar for : : my $bill = try { ack() CATCH { thpp() } }; : : but to me that code is concise enough that it doesn't warrant syntactic relief. Well, right now you can write either of: my $bill = try { ack() } // thpp(); my $bill = try { ack() } orelse thpp(); where the latter has the advantage of setting $! on the right to the return value of the try. We *could* go as far as making orelse carry try semantics, but I think a more appropriate solution might be to reduce the precedence of blockless try/do/gather etc. to below orelse. And then you could say my $bill = try ack() orelse try thpp() orelse do ppt(); But I need to think through the ramifications of such a precedence change. It would make blockless do rather pointless, for instance, but maybe that's a worthwhile tradeoff. And I'm using it above as a no-op to indicate a non-try. It might also be useful to have a try variant that only catches exceptions intended as benign failure to match. Or maybe try should have a way of indicating the severity of exception it wishes to trap by default. This could be a keyword variant, or an adverb, or a pragma. I'm thinking the pragma is most likely to distribute syntactic relief nicely over a lexical scope. In any case it would only set the default CATCH handler which could still be overridden in any particular try block. So, basically, we could just turn blockless try/gather/do into listops from a precedence point of view, though they'd still essentially be macros. This seems like a good thing. At worst it means you need to use parens: try($x if $y); if you need a statement as an argument, where currently you could write try $x if $y; But staring at those two, I'd say the parens are almost a feature from the least surprise viewpoint. Larry
Re: Catching exceptions with the // operator
Yaakov Belch perl6-at-yaakovnet.net |Perl 6| wrote: Let me explain why this is useful and why I think this is the right thing: First of all, it provides a very light-weight exception handling using well-known ideoms like: $file_content=read_file($filename) // $default_value; compute_statistics($data) // write_log_message(stats failed: $!); With the proposed change, these ideoms work whether the functions throw exceptions or not. You can change the meaning of fail to throw exceptions or to return the unthrown object which is an interesting value of undef. So under use fail 'return'; your line works as intended. This is what you should do if you are expecting failure in this case. A macro can give you some syntactic sugar for that: macro ttt ($x) is parsedexpression { return { use fail 'return'; $x; }; }