Re: When and how to throw exceptions?
On Fri, Apr 9, 2010 at 1:54 AM, Ovid publiustemp-moduleautho...@yahoo.comwrote: (Javaesque psuedo-code): try { customer.read(); } catch ( Exception::IO::NotFound e ) { ... } catch ( Exception::IO::CantOpen e ) { ... } catch ( Exception::IO::CantRead e ) { ... } catch ( Exception::IO e ) { ... } catch ( Exception e ) { panic(I don't know what type of exception I have! . e); } Yeah, the example is overkill :) The point here is that you try more specific exceptions first and drill down to less specific ones, including a catch-all exception. However, what happens if you get rid of the NotFound, CantOpen and CantRead? If the exception thrown is one of those types and assuming they're really subclasses of Exception::IO, then the Exception::IO catch will catch them rather the incorrectly falling through to the panic. If these were merely shoved in a hash, you'd have to try to fall back to parsing either the messages (or the key) to figure out that you don't really have a panic. I've never understood what's wrong with using string exceptions for this kind of thing. Maintaining a discipline about properly dieing is required both ways, and defined ($nextline = INPUT) or die IO CANTREAD: eof on INPUT?' seems more concise than abstracting an exception type hierarchy behind a class hierarchy only to unabstract it later: eval { $customer-read(); 1 } or do {{ # double openers to create block to last out of my $e = $@; $e =~ m/^io notfound/i and do { ...; last }; $e =~ m/^io CantOpen/i and do { ... ; last }; $e =~ m/^io CantRead/i and do { ... ; last }; $e =~ m/^io /i and do { ... ; last }; die $e UNEXPECTED; # filename, line number, newline added by die }}; Either way, you still need to maintain the meaning of the string prefixes or the various exception types in a project-wide data dictionary. I don't want to start (or restart) some kind of big argument; my understanding is that coders coming from a Java background simply prefer to use classes for everything.
Re: When and how to throw exceptions?
* Ovid publiustemp-moduleautho...@yahoo.com [2010-04-09 08:55]: Buut ... that's why you want those exceptions in classes instead of as hash keys. Of course. Just saying that if you *do* consider too many exception classes a problem in need of a solution, then using a field as a message selection key would at least preclude the need parse. Regards, -- Aristotle Pagaltzis // http://plasmasturm.org/
Re: When and how to throw exceptions?
* Ovid publiustemp-moduleautho...@yahoo.com [2010-04-07 09:05]: Presumably the format should try to determine the number of conversions in the format and perhaps the alias could generate a sub with a corresponding prototype like 'sub throw_io_read($$)'. That might give you a touch of compile-time safety. Haven't really thought too carefully about this, though. I think the best approach would be to tie String::Formatter into Exception::Class, specifically its `require_named_input` and `named_replace` options. To produce its message, an exception class would pass its declared fields as format inputs. Then resulting interface would look like this: use Exception::Class ( 'MyException', 'IOOpenRead' = { isa= 'MyException', alias = 'throw_io_open', fields = [ qw( file mode errno ) ], format = 'Cannot open %{file} for %{mode}ing: %{errno}', }, ); You’d use that like so: open my $fh, '', $filename or throw_io_open file = $filename, mode = 'read', errno = $!; Regards, -- Aristotle Pagaltzis // http://plasmasturm.org/
Re: When and how to throw exceptions?
Hi Ovid, On Wed, Apr 07, 2010 at 12:04:08AM -0700, Ovid wrote: --- On Wed, 7/4/10, Lutz Gehlen lrg...@gmx.net wrote: What I need a central place for is the definition of the actual error messages. With my module Exception::EasyThrow, I can write at the beginning of my module: use Exception::EasyThrow (var_ud = 'Variable %s is undefined. We are doomed.', ... = ...); and then later var_ud('foo') if(!defined($foo)); Note, in your code above, you can still have different error messages in different packages. You've made things a touch easier, but not really solved the underlying problem. I usually handle this by having a central module defining the error functions and offering them for import. What about this proposal for Exception::Class? package My::Exceptions; use Exception::Class ( 'MyException', 'IORead' = { isa= 'MyException', alias = 'throw_io_read', format = [ 'Cannot open %s for reading: %s' ], }, ); And then: use My::Exceptions 'throw_io_read'; open my $fh, '', $filename or throw_io_read $filename, $!; I think that an inclusion into Exception::Class is definitely worth thinking about. I even contacted Dave about it once, but I fear I wasn't very clear about what I had in mind, so he suggested that I publish my module first and he would consider it for Exception::Class. Dave, what do you think about Ovid's suggestion? One thing that always troubled me when I thought about a good way to add my desired behaviour to Exception::Class and that is also an issue with the 'format' suggestion is that I think that an own class for each message might be a bit of an overkill. If I think of exception hierarchies I rather think of a class like My::Exception::IO (instead of IORead in your example). A read failure would lead to an instance of the IO class with a message about the read failure. Therefore I went for a different way just for throwing the exception. I don't claim to have made up my mind completely about this. As mentioned before, I started this thread to raise a discussion. But this is why I did not go your way so far. Thanks for your comments, Lutz
Re: When and how to throw exceptions?
* Lutz Gehlen lrg...@gmx.net [2010-04-09 01:55]: I think that an own class for each message might be a bit of an overkill. Why? I guess that could be addressed by allowing a hash of formats and then offering the throwing site to pick one of them by setting a special field. The key here is that you want to avoid a situation where catching code has to parse the error message. Regards, -- Aristotle Pagaltzis // http://plasmasturm.org/
Re: When and how to throw exceptions?
Hi Aristotle, sorry for the delayed reply, I had some computer problems and then this topic slipped a bit out of my mind. On Friday 12 March 2010 23:33:40 Aristotle Pagaltzis wrote: * Lutz Gehlen lrg...@gmx.net [2010-02-21 01:40]: 1) The first question deals with how to throw exceptions properly. In projects which ask for a more sophisticated way than just carping or croaking I use Exception::Class. However, in both cases the error message has to be assembled when the error is thrown. given that Exception::Class already provides a central place to define your exception hierarchy along with messages,… I don’t follow what it is that you need another central place for? This might be one point where I don't understand how people use Exception::Class or how they throw exceptions at all. As I wrote in the original mail I might miss something here. I understand that I can build a hierarchy of exception classes using Exception::Class. But when I throw an exception I still have to assemble the error message there, e.g.: if(!defined($foo)) { My::Exception::Class-throw ('Variable foo is undefined. We are doomed.'); } As already shown by this simple example this is likely to span several lines thereby drawing unnecessary attention of the reader. This is mitigated a little bit by the alias parameter of Exception::Class. However, if I don't pay close attention I might write somewhere else: if(!defined($bar)) { My::Exception::Class-throw ('We are doomed because variable bar is undefined.'); } which is not how it should be. The same kind of error should produce the same message. What I need a central place for is the definition of the actual error messages. With my module Exception::EasyThrow, I can write at the beginning of my module: use Exception::EasyThrow (var_ud = 'Variable %s is undefined. We are doomed.', ... = ...); and then later var_ud('foo') if(!defined($foo)); which is often possible in one line. By default, it just croaks the message, but it can be set up to throw an Exception::Class object or do something else. Do you see what I mean? Is this possible with Exception::Class alone? Or is it stupid for some reason to want this? Thanks to everybody for advice Lutz
Re: When and how to throw exceptions?
Hi Lutz, * Lutz Gehlen lrg...@gmx.net [2010-02-21 01:40]: 1) The first question deals with how to throw exceptions properly. In projects which ask for a more sophisticated way than just carping or croaking I use Exception::Class. However, in both cases the error message has to be assembled when the error is thrown. given that Exception::Class already provides a central place to define your exception hierarchy along with messages,… I don’t follow what it is that you need another central place for? 2) My second question is how thorough I should be about throwing exceptions. You don’t want to check each and every possibility. As you said, if nothing else, it might needlessly prevent clever uses of your code, and most of the time you don’t gain much anyway. My question is if there are any general guidelines how to decide what to check and what not (depending on my general security requirements) or do I have to perform some gut-feeling appraisal of efficiency vs. security? There are basically two ways for errors to propagate: downward and sideways. • Downward propagation means you got something from the user, and you just passed it down through to something else without checking it. This is easy to debug: that’s what stack traces are for. Maybe the user passed something bad into your code and your code blew up. Then the error won’t be reported in their code, but a stack trace will tell them which call of theirs it was where they passed something nonsensical that caused your code to crash. • Sideways propagation means you processed a value in some way, and then used or returned the result without checking if it’s good. Eg. first (try to) you turn a filename into a filehandle, then you blindly pass the filehandle to a function. This is hard to debug. The value that caused the problem is not a value passed down directly, it is the result of some computation whose intermediate states do not show up in the stack trace. The trace doesn’t tell you why that bad value got to be bad in the first place. You have to step through the code in a debugger or add print statements to track what happened. I’m sure you can already imagine where this is going: basically, you should check your own intermediate computed values, to ensure you don’t propagate error conditions sideways between parts of your own code. But if you operate on user input directly, and the operation can blow up, you can leave that input unchecked for the purposes of error reporting. (You might of course still want to check it for other reasons, eg. security. That’s a different matter.) Regards, -- Aristotle Pagaltzis // http://plasmasturm.org/
Re: When and how to throw exceptions?
On Fri, 12 Mar 2010, Aristotle Pagaltzis wrote: I’m sure you can already imagine where this is going: basically, you should check your own intermediate computed values, to ensure you don’t propagate error conditions sideways between parts of your own code. But if you operate on user input directly, and the operation can blow up, you can leave that input unchecked for the purposes of error reporting. (You might of course still want to check it for other reasons, eg. security. That’s a different matter.) Another good reason to check it is to provide a good error message. For example, division by zero at line 116 is much less useful than the foo parameter to the frabble() method must be a positive integer. -dave /* http://VegGuide.org http://blog.urth.org Your guide to all that's veg House Absolute(ly Pointless) */
Re: When and how to throw exceptions?
--As of February 21, 2010 1:43:58 PM +1300, Lutz Gehlen is alleged to have said: Therefore I would like to define my error messages in some sprintf format way at a central place and use these templates when the time has come. I have written a module Exception::Class::EasyThrow that does this. However, I still have not published it. The reason is that I have the feeling that I don't know much about how to deal with exceptions in general and I wonder: If everybody would have my problem then there should be something on the CPAN already. And if other people don't have my problem how do they deal with the issues I have mentioned above? That's my first question. Personally, I use a system somewhat like that, but I don't expect that class to be general. If the point is that all exceptions are consistent in a project, I expect that class to be unique to the project. (And I quite often won't make the messages themselves too detailed: The exception says where it was thrown, so I use that to track where the problem is. The message is just for decoration if something were to get through to a user. It shouldn't be actually _used_ for anything.) 2) My second question is how thorough I should be about throwing exceptions. Ideally, I would think that it should not only be impossible that my module produces wrong results, but it should also be impossible to trigger an internal Perl error within my module. All the situations in which this would happen should be checked beforehand and a proper exception should be thrown. However, this goal raises several questions. To perform this thorough checks takes a lot of development time and possibly also quite some execution time. If I want to read in a long list of numbers, should I really check that also the 3972nd one passes a looks_like_number test? In general, should I really spend a lot of development and execution time on checking input that no sane user would provide? On the other hand, do I really want to claim to be able to anticipate every sane scenario that a user might come up with? Apart from that, is it really desirable to avoid internal perl errors in any case? If Perl would die with Division by 0 is it necessary to throw an exception a bit earlier which says Naughty naughty! If I wouldn't have checked, perl would die with division by 0 in a moment.? However, what if perl bails out much later? Then debugging might be much easier when the error is detected right away. Mostly, what you are likely think of as 'internal Perl errors' here _are_ exceptions, in a sense. For instance, perl doesn't _die_ when it hits division by 0, it die()'s. (Or some equivalent.) If someone using your code is expecting exceptions and therefore calling your code in eval blocks, that will be caught. (In other words: 'eval { say 1/0; } or say I had an error.;' will return 'I had an error.', not a 'Division by 0' error.) So, basically, don't try to overdo it. Perl will handle some things appropriately for you. One of the basic rules I learned ages ago was 'Don't test for a condition you aren't going to handle.' Throw an exception if you expect the calling program to be able to handle the exception and recover in some way. If that's not really possible, it's appropriate to die, and tell the user they're insane. (Or, at least, that they've driven _you_ insane.) Daniel T. Staal --- This email copyright the author. Unless otherwise noted, you are expressly allowed to retransmit, quote, or otherwise use the contents for non-commercial purposes. This copyright will expire 5 years after the author's death, or in 30 years, whichever is longer, unless such a period is in excess of local copyright law. ---