hi,
is there any reason to not convert all errors that are being thrown anywhere in pike 7.7 to error objects?
if not, then i'd prefer to stomp on any that i come accross rather than keep using tests on the error type.
greetings, martin.
hi,
i found that the errors in question are coming from master()->error() i suspect that changing that function may not be a good idea as it may be used in user code.
however, i would still like to see error objects being used everywhere.
should another function be created and all pike code be combed with a search/replace?
greetings, martin.
I'd also like to see error objects instead of the old two-element arrays, but more importantly I'd like to see them properly typed. That means recognition constants like "is_charset_decode_error" in Locale.Charset.DecodeError, to take a recent example.
I see little point in batch converting all old style error arrays to similarly nondescript error objects, although I don't see any particular harm in it either.
i agree with this goal, so now i am mostly interested in figuring out how to get there without creating to much hassle for anyone.
i am not sure if it is possible to come up with proper error types for all errors in one batch. more likely this is done one error at a time. whenever someone comes across an errortype in their code.
i'd be happy to create new error types for the errors that my code is currently handling.
batch converting to Error.Generic would only be a sideffect from changing the error() function in the master.
if the goal of making proper types is followed through then this error() function could eventually be removed completely, so potentially use of it could be discouraged by some means.
the reason for changing error() to return an error object is to be sure that any error handling code can be sure to only have error objects to deal with. this may not be necessary if there is a simple way to test the error type that works with arrays too.
hmm, (object_program(error)==MySpecialError) appears to work and not complain if the error is an array.
i suppose there is no solution to make catching error easier and we still need to do:
mixed error = catch { ... }; if (object_program(error)==MySpecialError) //handle the error else throw(error);
catch(MySpecialError){ ... }; would be nice.
greetings, martin.
what would be involved to implement something like this?
catch(MySpecialError){ ... };
this syntax seems the most natural, although it might cause some problem to keep it seperate from
catch( ... );
but then. maybe not, since the suggested form is currently not valid syntax.
greetings, martin.
Well, an agreement on how the syntax would work. I think a try...catch...finally would be prettiest myself.
fine with me, i don't have a preference for any specific syntax, i care more about the functionality, and want to know what is involved in getting there.
i do get the impression occasionally that some features that seem hard to implement are really easy, and others that seem easy are actually rather complex.
in this case i suspect that the syntax is the easy part, so i'd rather not start a long discussion about the syntax until there is an assessment of the complexity of the functionality.
greetings, martin.
There has been a discussion earlier about this, of course. Don't feel like digging it up, but I believe my suggestion was something like:
try (mixed err) { // ... } catch (err->is_my_funky_error) { // ... } catch (err->is_foo_error && (err->failure_pos >= 4711 || has_value (err->message, "blah blah"))) { // ... } finally { // ... }
This follows the "looks-like" paradigm and provides adequate dynamic classification power.
Maybe the type system could be used to match the thrown error with the type of the error variable, so you could write e.g.
try (MyFunkyError err) { // ... } catch (1) { // ... }
and still catch only exceptions that are typewise compatible with MyFunkyError. The rest would be rethrown automatically. That way one wouldn't have to bother with old-style error arrays in most cases.
Seems fine to me. The second suggestion is a bit awkward, but it could work... but if the type system is possible to use like that, could it be possible to have type support like this?
try (Error err) {... } catch (MyFunkyError) { ... } catch (FooError) { ... } finally { ... }
It should be easy to see that the argument to catch is a constant type.
Looks a bit strange to me.. Maybe a more generic approach, where the catch clause is inspired by the for statement:
catch ([declaration] [; test]) { ... }
Then I can write dynamic stuff like
try { ... } catch (object err; err->is_my_error && err->failure_pos > 4711) { ... }
while all you inspired-by-statically-typed-languages folks can write
try { ... } catch (MyError err) { ... }
But it all hinges on that runtime type matching works well and is reasonably efficient (at least for objects).
Well, it doesn't have to be efficient. If there is no error, the code doesn't have to run, and most of the time there shouldn't be any error. (Right?)
A combination could work too, so you can put the declaration at "try" once and for all if you're in that mood,
try { ... } catch (object err; err->is_my_error && err->failure_pos > 4711) { ... }
<=>
try (object err) { ... } catch (; err->is_my_error && err->failure_pos > 4711) { ... }
and
try (object err) { ... } catch (MyError err) { ... } catch (; err->is_my_error && err->failure_pos > 4711) { ... }
(I'm more of the catch MyError type though, given that the typesystem works.)
If the typesystem can't support this, however, the branch of discussion goes to the bitbucket. :)
/.../ and most of the time there shouldn't be any error. (Right?)
There could be applications that throw some errors fairly frequently, so speed can't be ignored. Anyway, with the option to resort to the err->is_my_error variety, the type comparisons can always be avoided when performance is very important.
I can also point out that since the type checker in pike implements the "looks-like" philosophy (i.e. no inherit relation is necessary), all thrown error classes should include a unique is_foo_error constant. Otherwise two completely unrelated error could happen to be typewise compatible.
A combination could work too, so you can put the declaration at "try" once and for all if you're in that mood,
Possible, but I'm not sure it's worth the proliferation in syntax varieties just to save a little bit of typing. To that end I suggest to start only with the syntax where the declaration is in each catch clause and extend with the other one later on if people find it really annoying.
If the typesystem can't support this, however, the branch of discussion goes to the bitbucket. :)
Indeed. I leave that to the undisputed authority on the pike type system, namely Grubba. ;)
/.../ and most of the time there shouldn't be any error. (Right?)
There could be applications that throw some errors fairly frequently, so speed can't be ignored. Anyway, with the option to resort to the err->is_my_error variety, the type comparisons can always be avoided when performance is very important.
True.
A combination could work too, so you can put the declaration at "try" once and for all if you're in that mood,
Possible, but I'm not sure it's worth the proliferation in syntax varieties just to save a little bit of typing. To that end I suggest to start only with the syntax where the declaration is in each catch clause and extend with the other one later on if people find it really annoying.
Agreed, it would also lead to the exception object having the expected scope.
If the typesystem can't support this, however, the branch of discussion goes to the bitbucket. :)
Indeed. I leave that to the undisputed authority on the pike type system, namely Grubba. ;)
I guess that's my cue.
Mast's suggested syntax:
try_statement: TOK_TRY block catch_list ;
catch_list: catch_statement | catch_list catch_statement ;
catch_statement: TOK_CATCH ( type optional_identifier ) block_or_semi ;
Is easy to generate code for:
if (mixed __err__ = catch { BLOCK }) { if (_typeof(__err__) <= TYPE) { TYPE OPTIONAL_IDENTIFIER = [TYPE] __err__; BLOCK_OR_SEMI } else if (...) { ... } else throw(__err__); }
I don't however see any immediate need for the syntax in the complex case:
try { ... } catch (object err; err->is_my_error && err->failure_pos > 4711) { ... }
which could just as well be written as:
try { ... } catch (object err) { if (err->is_my_error && err->failure_pos > 4711) { ... } else throw(err); }
/.../ and most of the time there shouldn't be any error. (Right?)
There could be applications that throw some errors fairly frequently, so speed can't be ignored. Anyway, with the option to resort to the err->is_my_error variety, the type comparisons can always be avoided when performance is very important.
True.
Throwing errors slowly is a feature. It discourages the use of errors as a signaling mechanism in the normal program flow.
"Peter Bortas @ Pike developers forum" 10353@lyskom.lysator.liu.se wrote:
/.../ and most of the time there shouldn't be any error. (Right?)
There could be applications that throw some errors fairly frequently, so speed can't be ignored. Anyway, with the option to resort to the err->is_my_error variety, the type comparisons can always be avoided when performance is very important.
True.
Throwing errors slowly is a feature. It discourages the use of errors as a signaling mechanism in the normal program flow.
Why a religious stance on this issue? Most of pike seems to be designed to let people use whatever language features they want as they see fit. I think that's a better way to go, rather than trying to push a "right" way of doing things like some other languages do.
Adam
Throwing errors slowly is a feature. It discourages the use of errors as a signaling mechanism in the normal program flow.
Why a religious stance on this issue?
Because he does not want to read code that uses exceptions to test for any conditions, and worse still, code to APIs designed by others, that require him to do so himself as well. Presumably, anyway. (It's my own stance.)
"Johan Sundstr_m (Achtung Liebe!) @ Pike (-) developers forum" 10353@lyskom.lysator.liu.se wrote:
Throwing errors slowly is a feature. It discourages the use of errors as a signaling mechanism in the normal program flow.
Why a religious stance on this issue?
Because he does not want to read code that uses exceptions to test for any conditions, and worse still, code to APIs designed by others, that require him to do so himself as well. Presumably, anyway. (It's my own stance.)
Lots of people want and don't want lots of things. Why is only this particular issue being treated this way? You don't have to use other people's code if you don't want to, and the "official" pike modules are welcome to have style guidelines so that you don't have to deal with any code that doesn't meet those guidelines.
If pike doesn't try to push any particular agenda in the dozens of other areas where people disagree, why should it in this case?
Adam
I'm explaining the position, not arguing the point. It is advice that is commonly served in most programming circuits, to use exceptions for exceptional circumstances only, and holding that position, it is not a misfeature that exception handling is not the most heavily optimized aspect of the language.
It might be some consolation to people holding a contrary position, that generating backtraces _is_ one of the more heavily optimized aspects of the language, since that was a hot spot that came up when running the pike test suite, which was used as test material for an optimization sweep a few years ago.
You shouldn't assume "people" shares Zinos view and "treat" it special. I do not share it, for instance. There's no borg-like "people" group here.
I regard throw/catch as a language construct just like any other. If it's the right tool for the job, I use it, otherwise not. If that means it gets used a lot or seldom has very little relevance. It should of course be as fast as is possible.
Anyway, it is important to carefully consider which errors to throw and which to only return some kind of error value for (but performance should preferably not be a deciding factor). Getting it wrong in either direction makes the interface annoying to use.
As an example of an interface that throws too few exceptions, I can mention most of the basic I/O functions (mv, cp, Stdio.File.open, to name a few). It's annoying having to check errno after each and every call, so many (me included) tend to sloppily ignore that, which leads to bad code. All kinds of I/O errors shouldn't be signalled the same way imo.
On Fri, Jun 01, 2007 at 06:40:01PM +0000, Henrik Grubbstr�m (Lysator) @ Pike (-) developers forum wrote:
Mast's suggested syntax: try_statement: TOK_TRY block catch_list ; catch_list: catch_statement | catch_list catch_statement ; catch_statement: TOK_CATCH ( type optional_identifier ) block_or_semi ; Is easy to generate code for:
nice!
I don't however see any immediate need for the syntax in the complex case: try { ... } catch (object err; err->is_my_error && err->failure_pos > 4711) { ... }
class ComplexError { int is_my_error = 1; int(4711..) failure_pos; }
try { ... } catch (ComplexError) { .. }
ok, it's a bit more expensive but this should work, shouldn't it?
greetings, martin.
which could just as well be written as:
try { ... } catch (object err) { if (err->is_my_error && err->failure_pos > 4711) { ... } else throw(err); }
That looks exactly like the example I used in a different part of the thread to show how bad it gets when there's no dynamic test feature in the catch clause, so I guess I need to argue that point a bit more:
1. It's a step backward to the current situation where you have to catch more errors than you want. The risk is of course to miss/forget/ignore rethrowing the unwanted errors.
In the worst case, where one for some reason doesn't want to type the error object harder than "object", there'd be no gain at all from the current situation.
2. You get the nested-else-problem. "throw(err)" is not really what you want to do in that else clause. Rather you want to go on trying the next catch clause, but that's not syntactically possible.
unhandled errors should definetly be rethrown automaticly. it looks like catch could act like a switch statement with an implicit default clause...
greetings, martin.
In my experience, there are two different sorts of exceptions:
1. Those that one might want to handle: Errors on invalid input of different sorts, the more common I/O errors, etc. 2. Errors that one typically never wants to handle: Argument errors, indexing errors, etc.
It's those in the first category that needs typing, those in the second does not. Also, the errors in the first category are way fewer.
In fact, the vast majority of all error() calls in Pike and Pike_error() calls in C fall into the second category. I think those two functions are perfectly fine to use in such cases. They're lots easier to use than typed error objects, especially on the C level. (Creating typed errors in C is a real pain. It could probably be improved a bit by better support functions, but it can never get as simple as a Pike_error call.)
I therefore think that the right approach is to fix typing for the fairly few errors where there's a need for it and simply leave the rest untyped. That's done module-by-module and error-by-error, of course.
I don't believe (anymore) in a fancy error hierarchy that tries to encompass both categories. I've simply never felt any use for that.
So imo both error() and Pike_error() should (nay, must) stay, but they could be changed to throw Error.Generic objects instead.
i'd be happy to create new error types for the errors that my code is currently handling.
I say don't sweat it. Converting all the odd errors of the second category above is probably just a waste of time.
the reason for changing error() to return an error object is to be sure that any error handling code can be sure to only have error objects to deal with. /.../
Yep, that would afaics be the only reason for doing it (not implying that it's an insignificant reason). But also remember that anywhere where you aren't sure to deal only with newer code, you must play safe and assume that catch() still might return an array (or anything else, for that matter; well written catch code should never fail itself on an odd error type since that'd clobber the real error).
hmm, (object_program(error)==MySpecialError) appears to work and not complain if the error is an array.
Strange. I consider that a bug, actually. Anyway, that way of testing an error isn't very good.
On Wed, May 30, 2007 at 05:45:01PM +0000, Martin Stjernholm, Roxen IS @ Pike developers forum wrote:
So imo both error() and Pike_error() should (nay, must) stay, but they could be changed to throw Error.Generic objects instead.
ok.
i'd be happy to create new error types for the errors that my code is currently handling.
I say don't sweat it. Converting all the odd errors of the second category above is probably just a waste of time.
i did not intend to do that. i only plan to change the errors that i need to handle in my code.
But also remember that anywhere where you aren't sure to deal only with newer code, you must play safe and assume that catch() still might return an array (or anything else, for that matter; well written catch code should never fail itself on an odd error type since that'd clobber the real error).
would it make sense to have catch always return an error object? or respectively, change throw to always create an object out of the arguments it receives?
hmm, (object_program(error)==MySpecialError) appears to work and not complain if the error is an array.
Strange. I consider that a bug, actually. Anyway, that way of testing an error isn't very good.
you mean it should throw if i use object_program() on an array? (actually, i think it should return an Array class, but that's another topic).
what is a better way to test the error type? (objectp(error) && error->error_type==MySpecialError.error_type) or (objectp(error) && error->is_my_special_error) ?
for some reason i'd like to avoid the use of objectp() but i guess it is not any worse than using object_program()
greetings, martin.
(objectp(error) && error->error_type==MySpecialError.error_type) or (objectp(error) && error->is_my_special_error)
I assume that error could be several types at once, so the last one would probably work best, if there isn't any function X X(error,MySpecialError) that checks if the value of error fits into the type MySpecialError.
good point. i have been thinking about such a special function to test errors, as it could throw if the test fails, although this doesn't work if you want to handle multiple errors seperately.
for that a new syntax like try,catch,finally seems to be the best option. (anyone willing/able to comment on how much work that would be?
greetings, martin.
would it make sense to have catch always return an error object? or respectively, change throw to always create an object out of the arguments it receives?
I don't think that'd be worth the compatibility hassle. I've seen code throwing plain strings internally and catch them with a stringp() test. (I don't recommend that kind of stunt, but in some applications where errors are thrown very frequently it could be justifiable.)
you mean it should throw if i use object_program() on an array?
Yes, or on anything else that isn't an undestructed object.
(objectp(error) && error->is_my_special_error)
This is the kind I prefer since it gives the thrower complete control over thrown error object: It's possible to combine error classes, and to construct an own error class that not even inherits the original one (which might be difficult in dynamically compiled environments). It also allows quick-and-dirty code that simply defines a constant to trig a specific catch somewhere, without having to implement an entirely-fledged compatible error object.
unhandled errors should definetly be rethrown automaticly.
Yes, definitely. That's why it's important to provide decent expressive power in the catch clause tests. You don't want to force people to resort to stuff like this:
try {...} catch (MyError err) { if (err->failure_pos >= 4711) { ... } else throw (err); }
pike-devel@lists.lysator.liu.se