February 19, 2012
On 2/19/12 4:30 AM, Juan Manuel Cabo wrote:
> Now, expanding on the hierarchy: I said that it makes no sense to
> subclass UnrecoverableExceptions. Recoverable exceptions on the other
> hand, need to be subclassed _with_the_catch_on_your_mind_. You are
> passing info from the throw site to the catch site. The catch block is
> the interpreter of the info, the observer. You are communicating
> something to the catch block.
>
> So please, do not create a new types if there is no value in writing a
> catch that only cathes that exception and that can recover from that
> exception. Otherwise, use an existing type.

I think this is a very sensible point. I wonder to what extent it clashes with a simple idea that just occurred to me. It would be very easy to define a hierarchy as follows:

class ModuleException(string theModule) : PackageException(packageOf(theModule))
{
    ...
}

Then any module could issue

throw new ModuleException!(.stringof);

That way the exception hierarchy would parallel the module hierarchy. (A module is free to define more specific exception inheriting ModuleException!(.stringof)).

The catcher would catch things like ModuleException!"acme.text.io" and such.

In that approach, all exceptions thrown from Phobos would inherit PackageException!"std" and all exceptions thrown from std.getopt would be (or inherit) ModuleException!"std.getopt".

This would save a lot on the boilerplate while still keeping open the option of defining fine-grained hierarchies. Any thoughts, please chime.


Andrei
February 19, 2012
On 2/18/2012 10:50 PM, Andrei Alexandrescu wrote:
> On 2/18/12 11:09 PM, Jim Hewes wrote:
>
> I think there's a bit of a confusion there. In fact, I dedicated two
> distinct chapters to error handling and contract programming in TDPL in
> an attempt to dispel it.
>
> Andrei
>

Sorry, I have not read your book. At least I can say that if you felt there was a need to dispel something then I must not be the only one who thinks this. :)

Jim
February 19, 2012
Thanks for the interest!!, Not sure when, but I'll write a translation.
--jm

On Sunday, 19 February 2012 at 15:57:25 UTC, Andrei Alexandrescu wrote:
> On 2/19/12 4:30 AM, Juan Manuel Cabo wrote:
>> Hello D community! This is my first post!! I hope I can bring clarity to
>> all this. If not, I apologize.
> [snip]
>
> Thanks for an insightful post. If you found the time, it would be great if you could translate your paper on exceptions from Spanish.
>
> Andrei


February 19, 2012
On 02/19/2012 05:00 PM, Andrei Alexandrescu wrote:
> On 2/19/12 3:17 AM, Jonathan M Davis wrote:
>> "As much information as possible" is way more than a transient
>> property. If my
>> code is going to retry or do something else or give up, it needs enough
>> information to know what went wrong, not just whether the function
>> which was
>> called think it might work on a second try.
>>
>> Having an exception hierarchy provides some of that information simply
>> with
>> the types, and makes it easier to organize code based on what went
>> wrong (e.g.
>> separate catch blocks for each type of exception). And having that
>> hierarchy
>> also means that the derived types can have additional information
>> beyond their
>> type which could be useful but is specific to that problem and so
>> wouldn't make
>> sense on a general exception type.
>>
>> I really don't see what transient buys you in comparison to that.
>
> A notion of transiency planted fundamentally in all exceptions allows
> one to act on it regardless of origin and hierarchy.
>
> Andrei
>

Transiency is a powerful concept at the handler side, but the interface it is difficult to fulfil at the point where the actual error occurs. What is important is probably not whether or not transiency is useful if it is there, but more whether or not a sufficient part of the useful exceptions are naturally transient. This is what I doubt. OTOH, as I understand it, introducing the concept would require additional boilerplate in most exception handlers.
February 19, 2012
On 18/02/2012 23:13, Andrei Alexandrescu wrote:
> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
> GetOptException
> FlagArgumentMissingException
> InvalidFlagArgumentException
> UnknownFlagException
> FileException
> FileNotFoundException
> NotFileException
> NotDirException
> AccessDeniedException
>
> I died inside a little.
>
> Andrei

Perhaps something like:

class PhobosException : Exception
{
    this(string _module, uint flags) { ... }
}

try
{
    doSomething();
}
catch(PhobosException e)
{
    // An exception was thrown by phobos
    if (e.moduleOf == "file") {
        // An exception was thrown by std.file
        if (e.flags & FileException.NotFound) {
            // File wasn't found
        }
    } else if (e.moduleOf == "stdio") {
        // An exception was thrown by std.stdio
    }
    // We only want to handle file and stdio exceptions, rethrow
    throw e;
}

Some pros/cons:
 * Catch-all for all phobos exceptions
 * No binary bloat from having lots of exception classes
 * Can still handle specific exceptions
 * e.flags isn't type-safe
 * It's not particularly pretty
 * Can only have up to 32 different "exceptions" in a module (64 if you make it a ulong)
 * It looks stupid if you come from an OOP language like java and you're used to having 5000 catch blocks
 * No need to decide on any exception hierarchy, just throw a PhobosException

-- 
Robert
http://octarineparrot.com/
February 19, 2012
On 2/18/2012 10:21 PM, Robert Jacques wrote:
>
> Not to jump on you in particular :) but bad user parameters should never
> be treated as exceptional. Even bad 'internal' parameters that are
> passed via the external API aren't exceptional. Programmers being lazy
> about input parameter checking is how hackers make their money.
>
> [snip]
>

I imagine that you would disagree with the design of the .NET Framework then, which uses ArgumentException all over.
http://msdn.microsoft.com/en-us/library/system.argumentexception.aspx

There's also a similar exception defined in the C++ Std Lib.

So what do you do when a bad parameter is passed? Return an error code in a return value? So you use both exception handling and return codes? How about bad parameters to constructors (in C++)?

You may say that bad parameters are not “exceptional”. OK. But my whole point of that paragraph there was that if you're going to declare one particular error is “exceptional” and another is not in a seemingly arbitrary way, then you need to tell us, for all errors, which are “exceptional” and which are not. You need a more strict definition of “exceptional” so that people know when to use an exception and when not to. Then in the cases they are not supposed to use an exception, what do they do?

>
> Yes, the USB stack has a high-level layer that can recover from a
> connection loss, but the rest of the protocol stack above and below it
> can't understand it and presumably ignore it. And you have just
> introduced a fairly long range dependency between the lowest level of
> your code and middle layer. (To say nothing of the implicit dependences
> implied with the intervening layers). This might be the best/only
> solution to the problem, but it also makes the code base larger and more
> fragile. I wasn't speaking in absolutes, but in relative difficulties.
>

I guess I don't understand what you're saying here. It sounds like you're arguing against exceptions in general, which I'm sure you're not. My stack *does* know what a connection loss is at any level. Just like it would know what an out-of-memory error is at any level. It just doesn't want to deal with it everywhere. By not handling this exception in the intermediate layers and rather just making them exception safe, the code is smaller not larger. Even at the topmost level it's known what a connection loss is, and the corrective action is to close the interface and reopen when the device is plugged in again.

>
> Traditional error codes are enums and so what your describe as
> BadParameter is an exception wrapping an error code. I really hope no
> one has been proposing to map error code values to exceptions on a 1:1
> basis.

Maybe not 1:1, but you talked about the need to have a large number of final classes. That's what I was thinking of.

> But your making many of my points; exceptions are (better,
> faster, stronger) error codes.

Yes. But not just that. Exceptions allow you to *not* write error handling code everywhere so that the normal program flow is easier to read. This is assuming you're writing exception safe code.

> My issue regarding the weak 'is a'
> relationship stems from the extra information in any given final typed
> exception being very specific to that particular exception. So its hard
> for me to see the rational between treating some of these as a group
> while not all of them: if MyGroupException is providing no more
> information then Exception, why can a function recover a
> MyGroupException and not a general Exception?

Borrowing part of the the exception hierarchy from another post in this thread...

Error
 +--Exception
     +--GetOptException (bad name, I prefer CommandLineException)
     +--FileException
     +--IOException (OK, I added this branch, just to show the idea)
         +--ReadErrorException
         +--WriteErrorException

A ReadErrorException 'is a ' IOException which 'is a' Exception.At some point or level in the code, I may want to handle IOExceptions because I know what to do with those, but not FileExceptions because I don't know what to do with those. If I were to catch Exception, then I would be responsible for re-throwing FileException and any other non-IOException, which I don't want to have to do all over.

I think a class hierarchy for exceptions is justified. As an analogue, say that you have a GUI class hierarchy where you have a Window class and various kinds of derived window subclasses. You will probably write a function at some point that takes a WindowPtr as a parameter and works on any type of window (but not any class in the whole library). A catch()  would work on a similar idea as the function handling Windows, catching all IOExceptions but not any and all exceptions in the hierarchy.

I think it is about having a balance between the number of different exception types and the attributes or error codes you put into them. You may decide that there are too many different types derived from IOException and you always end up catching them all anyway. So you can get rid of the ReadErrorException and WriteErrorException and instead just make these as error codes you can extract from the IOException class. But still, IOException is different from Exception and FileException. And I'm assuming that MyGroupException would be different from Exception in a similar way. Maybe it's not a perfect solution, but what is better?

Jim
February 19, 2012
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:jhr81v$2i3r$3@digitalmars.com...
> On 2/19/12 9:56 AM, Nick Sabalausky wrote:
>> "Andrei Alexandrescu"<SeeWebsiteForEmail@erdani.org>  wrote in message news:jhr0vq$24t0$1@digitalmars.com...
>>>
>>> This is self-evident. Again, the meaning of "recoverable" is "operation
>>> may succeed if retried with the same input". It's a hint for the catch
>>> code. Of course the program is free to ignore that aspect, retry a
>>> number
>>> of times, log, display user feedback, and so on. But as far as
>>> definition
>>> goes the notion is cut and dried.
>>>
>>
>> WTF? "Recoverable" means "can be recovered from". Period. The term
>> doesn't
>> have a damn thing to do with "how", even in the context of exceptions. It
>> *never* has. If you meant it as "operation may succeed if retried with
>> the
>> same input", then fine, but don't pretend that *your* arbitrary
>> definition
>> is "cut and dried".
>
> I think it's a reasonable definition of "can be recovered from" in the context of exceptions.
>

Reasonable maybe, but not obvious. That's all I'm trying to say.


February 19, 2012
"Nick Sabalausky" <a@a.a> wrote:
> "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:jhr81v$2i3r$3@digitalmars.com...
>> On 2/19/12 9:56 AM, Nick Sabalausky wrote:
>>> "Andrei Alexandrescu"<SeeWebsiteForEmail@erdani.org>  wrote in message news:jhr0vq$24t0$1@digitalmars.com...
>>>> 
>>>> This is self-evident. Again, the meaning of "recoverable" is "operation
>>>> may succeed if retried with the same input". It's a hint for the catch
>>>> code. Of course the program is free to ignore that aspect, retry a
>>>> number
>>>> of times, log, display user feedback, and so on. But as far as
>>>> definition
>>>> goes the notion is cut and dried.
>>>> 
>>> 
>>> WTF? "Recoverable" means "can be recovered from". Period. The term
>>> doesn't
>>> have a damn thing to do with "how", even in the context of exceptions. It
>>> *never* has. If you meant it as "operation may succeed if retried with
>>> the
>>> same input", then fine, but don't pretend that *your* arbitrary
>>> definition
>>> is "cut and dried".
>> 
>> I think it's a reasonable definition of "can be recovered from" in the context of exceptions.
>> 
> 
> Reasonable maybe, but not obvious. That's all I'm trying to say.

I guess "transient" is more descriptive.

Andrei
February 19, 2012
"Robert Clipsham" <robert@octarineparrot.com> wrote in message news:jhref7$bo$1@digitalmars.com...
> On 18/02/2012 23:13, Andrei Alexandrescu wrote:
>> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
>> GetOptException
>> FlagArgumentMissingException
>> InvalidFlagArgumentException
>> UnknownFlagException
>> FileException
>> FileNotFoundException
>> NotFileException
>> NotDirException
>> AccessDeniedException
>>
>> I died inside a little.
>>
>> Andrei
>
> Perhaps something like:
>
> class PhobosException : Exception
> {
>     this(string _module, uint flags) { ... }
> }
>
> try
> {
>     doSomething();
> }
> catch(PhobosException e)
> {
>     // An exception was thrown by phobos
>     if (e.moduleOf == "file") {
>         // An exception was thrown by std.file
>         if (e.flags & FileException.NotFound) {
>             // File wasn't found
>         }
>     } else if (e.moduleOf == "stdio") {
>         // An exception was thrown by std.stdio
>     }
>     // We only want to handle file and stdio exceptions, rethrow
>     throw e;
> }
>
> Some pros/cons:
>  * Catch-all for all phobos exceptions
>  * No binary bloat from having lots of exception classes
>  * Can still handle specific exceptions
>  * e.flags isn't type-safe
>  * It's not particularly pretty
>  * Can only have up to 32 different "exceptions" in a module (64 if you
> make it a ulong)
>  * It looks stupid if you come from an OOP language like java and you're
> used to having 5000 catch blocks
>  * No need to decide on any exception hierarchy, just throw a
> PhobosException
>

No, exceptions need to be based around semantics, not modules. The former is reusable and is well-proven to be very useful. The latter isn't particularly useful in general. And if anyone does have use for it, we could just add "moduleOf" to Exception, no need for it to be phobos-specific.



February 19, 2012
On 2/19/2012 3:40 AM, deadalnix wrote:
> Le 19/02/2012 06:09, Jim Hewes a écrit :
>
> Well, I think you are messing up between contract and exception. Wrong
> parameters is a contract problem, not an exceptionnal situation.
>
> The exemple you cited below is way more suited for an exception :
> someone unplug the USB device, then I trhow an Exception and it will
> cross all layers of my code to reach a point where it is recoverable.
>
> Except that contract detail, your post make perfect sense.

Then what do you do when bad parameters are passed? Do you return error codes using the function return mechanism? Do you use error codes for some errors and exceptions for others? Why? What's the strictly defined criteria for “exceptional” so that anyone can apply it to their own development and know when to use one or the other? If you use return codes for bad parameters, then does the caller always need to write error handling code to check for the bad parameter error?

I did point out that internal and external bad parameters are a different case. If you're talking about an exposed API for others to use, and it's something like a C interface from a DLL where you don't want to propagate exceptions, then of course you would return an error code or something similar. But if you're writing a function that you're going to call with your own code, then I'd say asserts can be used. And if it's a library that's meant to be called by others from the outside, but by the same language, then I say you can use exceptions. Examples are the argument exceptions in the C++ Std Lib and the .NET Framework.

I think the fact that these things are called exceptions, and that people then derive the word "exceptional" from that is misleading. But I know these types of things are always debated as religion and I suppose they will be for a while. :)

Jim


P.S. Sorry, I tend to use 'parameter' and 'argument' interchangeably and probably should not.