|There's more than one way to do things|
I'll respond selectively.
I apologize, you pretty much said all of TheDamian's arguments for exception objects are arguments for how to better use them for complex flow of control. I still think that statement is overly generalized and too prescriptive of intention, but perhaps TheDamian does argue for complex exceptional flow control...
By my reading of PBP, the cited benefits of objects over strings have to do with how easy it is to postprocess exceptions in code. His rules on the topic have to do with the advantages for handlers that are trapping exceptions, the best ways to write those handlers and the best way to structure exception class hierarchies. Those are all appropriate to how to use exceptions for complex flow of control. Oh, he also suggests that exceptions should be thrown on all failures, including recoverable ones, which should then be trapped. (The latter choice affects the other choices he makes. Many of his best practices hang together like this - which is why it is a bad idea to pick and choose individual suggestions without a good understanding of why they are made.)
My understanding of those rules and what he wrote is indeed as a series of arguments for how to better use them for complex flow of control. Specifically for flow of control where you are throwing exceptions, catching them in handlers, and the handlers are then making complex decisions on what to do with the exceptions.
When you know the code that is going to handle, or not handle, your exceptions you are not in the situation in which exceptions shine. Exceptions are all about passing control to unknown code.
Yes, that is what they do. That is not what they're for though.
The point of exceptions is to provide a standard way to handle exceptional events without having to litter your code with gotos or checks of returns. Those are the common alternatives in languages without exceptions. Exceptions are better than checking return because it is less error prone. They are better than goto because they lend themselves to clear mental models of what will happen in a particularly important set of circumstances. My preference is to settle on a style that makes that clear mental model as clear and simple as possible.
The problem with string exceptions in Perl is that they are tedious and error prone to identify. Strings must be controlled by convention, Perl can help in this control if typed exceptions are used. Strings are brittle and unhelpful when you need to present your user with info that varies with some context far removed from the error. They just don't scale well. It only takes the need to discriminate a single exception from the many to start feeling this problem.
Let's compare problems. If you use object based exceptions, one problem is that typos will mean that at the point where you're hoping to capture that critical piece of information about an error you'll instead find out that, due to a typo, you lost your information. That seems to me to be a tragedy. You complain about how hard it is to identify strings. By contrast I want a unique enough error message that I can find where it is generated by using grep. Furthermore I'd like strings to be descriptive enough that they document what went wrong. You complain about how to present the information in the exception to users. I'd prefer to be uninformative to users, but detailed to developers. YMMV but I've not had problems with the scalability of exceptions in projects with 100K lines of code. And if it comes to wanting to discriminate one exception, my first question is why you want to do that (sample good reason that I've needd, for an alarm timeout), and my second approach is that it is trivial to include a unique string in the exception. (The first approach that comes to mind is to write a brief explanation in the error, then grep for that explanation. But one could also include a random string.)
You and I could probably get by with printing a message and exiting instead of dieing. This would also make explicit that handling was not intended...
You could, I could not.
I want my file and line number captured. This is far more important to me than adhering to someone else's notion of good style.
I hope you didn't think this code sample was going to change my mind?
Looking at it, what I see is a bunch of code to do the same thing that calling confess does, but less reliably. Oh, it also starts the stack from an internal function call for no real reason. You're being encouraged to separate messages from the place where they are generated, making you less likely to produce a useful message (you know, one with $!, arguments, a statement of what you were trying to do..). The default message is uninformative And your fail() function won't accept an argument, so you're guaranteed to get the default message. Plus, as I mentioned before, if you have a bunch of these, a typo on a class name when you call the exception can leave you with a hidden landmine - you think you're going to get a useful message when you're not! (Of course I could also have a typo typing confess(), but since that is just one word and my fingers are very used to typing it, I'm unlikely to mess that up. I'm far more likely to mess up class names.)
To recap, here is a summary of my position.
For the simple case, objects are worse than strings. If you want to be more complex, then objects are an obvious win. I'd prefer to KISS, and would like to discourage complexity. (Admittedly, there are applications where that complexity is needed, but I'm not dealing with any.)