October 23, 2008
Thu, 23 Oct 2008 08:45:45 -0500,
Andrei Alexandrescu wrote:
> Sergey Gromov wrote:
> > Wed, 22 Oct 2008 19:18:57 -0400,
> > Jarrett Billingsley wrote:
> >> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
> >>> Errors represent situations which are typically non-recoverable--program logic errors, for example, or situations where data corruption may have occurred--while Exceptions represent the bulk of normal execution errors, including OutOfMemory conditions.
> >> How, pray tell, is an app supposed to recover from an out-of-memory condition?
> > 
> > In an image editor, a user asks to create a huge image (50k x 50k).  You try, run out of memory, and gracefully tell the user that the image was too big and you didn't succeed.
> 
> I could extract many instances of this pattern from my programs. Unfortunately, current bugs in the compiler, phobos, or both lead to sudden death of the application under certain circumstances. I hope one day I'll get around to investigating that.

I've noticed that failed new sometimes simply returned null instead of throwing an exception.  I remember it happened in a 3-line piece of test code.
October 23, 2008
Thu, 23 Oct 2008 08:43:04 -0500,
Andrei Alexandrescu wrote:
> Robert Fraser wrote:
> > Option B:
> > ---------
> > try
> > {
> >     new Socket(30587);
> > }
> > catch(Exception e)
> > {
> >     if(e.type == ExceptionType.Socket)
> >         printf("Could not open socket\n");
> >     else
> >         throw e;
> > }
> 
> I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.
> 
> That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.

If you try every function separately, yes.  But I think that the line between recoverable and non-recoverable exceptions is arbitrary and depends on the situation.  It makes sense to try/catch a transaction, not separate calls.  I can retry connection transaction if the socket open fails, but the whole upper-level transaction should fail if there is a database inconsistency or out of memory.
October 23, 2008
Sergey Gromov wrote:
> Thu, 23 Oct 2008 08:43:04 -0500,
> Andrei Alexandrescu wrote:
>> Robert Fraser wrote:
>>> Option B:
>>> ---------
>>> try
>>> {
>>>     new Socket(30587);
>>> }
>>> catch(Exception e)
>>> {
>>>     if(e.type == ExceptionType.Socket)
>>>         printf("Could not open socket\n");
>>>     else
>>>         throw e;
>>> }
>> I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.
>>
>> That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.
> 
> If you try every function separately, yes.  But I think that the line between recoverable and non-recoverable exceptions is arbitrary and depends on the situation.  It makes sense to try/catch a transaction, not separate calls.  I can retry connection transaction if the socket open fails, but the whole upper-level transaction should fail if there is a database inconsistency or out of memory.

I agree, but there's no need for a million types to support that.

Andrei
October 23, 2008
"Andrei Alexandrescu" wrote
> Sergey Gromov wrote:
>> Thu, 23 Oct 2008 08:43:04 -0500,
>> Andrei Alexandrescu wrote:
>>> Robert Fraser wrote:
>>>> Option B:
>>>> ---------
>>>> try
>>>> {
>>>>     new Socket(30587);
>>>> }
>>>> catch(Exception e)
>>>> {
>>>>     if(e.type == ExceptionType.Socket)
>>>>         printf("Could not open socket\n");
>>>>     else
>>>>         throw e;
>>>> }
>>> I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.
>>>
>>> That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.
>>
>> If you try every function separately, yes.  But I think that the line between recoverable and non-recoverable exceptions is arbitrary and depends on the situation.  It makes sense to try/catch a transaction, not separate calls.  I can retry connection transaction if the socket open fails, but the whole upper-level transaction should fail if there is a database inconsistency or out of memory.
>
> I agree, but there's no need for a million types to support that.

I agree that there's both a reason for subclassing and a reason for parameterizing.

I don't want only one Exception class in the whole hierarchy that is parameterized as given in the example, but I also hate it when the reason for the exception is only implemented by the type of the exception.

A good example is many OS errors, those should be parameterized on the errno (and should provide an easy way to build the string from strerror).

-Steve


October 23, 2008
Steven Schveighoffer wrote:
> A good example is many OS errors, those should be parameterized on the errno (and should provide an easy way to build the string from strerror).

Yah, that's defined a couple of times in Phobos already, one is StdioException and the other I forgot... so silly.

Andrei
October 23, 2008
Sergey Gromov wrote:
> 
> I've noticed that failed new sometimes simply returned null instead of throwing an exception.  I remember it happened in a 3-line piece of test code.

I think I've corrected all these in druntime.  if I haven't please let me know.


Sean
October 23, 2008
Thu, 23 Oct 2008 08:30:31 -0700,
Sean Kelly wrote:
> Sergey Gromov wrote:
> > 
> > I've noticed that failed new sometimes simply returned null instead of throwing an exception.  I remember it happened in a 3-line piece of test code.
> 
> I think I've corrected all these in druntime.  if I haven't please let me know.

I wasn't using druntime nor Tango when I stumbled upon that.  I'm glad to hear that those issues were addressed.
October 23, 2008
Andrei Alexandrescu wrote:
> Robert Fraser wrote:
>> Andrei Alexandrescu wrote:
>>> Jarrett Billingsley wrote:
>>>> On Wed, Oct 22, 2008 at 6:49 AM, Jacob Carlborg <doobnet@gmail.com> wrote:
>>>>> I think the name ArrayBoundsException should be changed to a more general
>>>>> name like BoundsException, OutOfBoundsException or
>>>>> IndexOutOfBoundsException. Then you can use the exception in every class
>>>>> that have some sort of index operation and not just for an array/array
>>>>> class.
>>>>>
>>>>
>>>> 2nded.
>>>
>>> I agree. In fact I wanted to ask you all the following question. What do
>>> you think about the current exception hierarchy in phobos? I think it is
>>> terrible. Each module in std you open, the first piece of code to be
>>> seen is the "class ThisModuleNameException" definition. In many (most?)
>>> cases the module-specific exception does absolutely nothing in addition
>>> to its base class. The putative reader (including me) tends to scroll non-critically over that passage without even blinking, mumbling in a trance - of course, yes, each module should define at least one exception type.
>>>
>>> Until one day when you stop scrolling and say, wait a minute. This all is repetition. And there are alternatives to catching by type - you can catch the base type and consult a field. And in fact I don't remember seeing code that depends on exceptions thrown from different modules having different types. There's something wrong here!
>>>
>>> I think most exception classes in phobos should be yanked if it's possible for their functionality (often nil) to be moved in the Exception base class. The module name should be a member. If someone needs to deal with an exception thrown from a specific module, they can always inspect the field. We don't need a huge hierarchy for that.
>>>
>>>
>>> Andrei
>>
>> Yes, you _could_ use a field... but the "catch a subclass" style is already there and is supported by the language, so _why_ use a field? Which of the following is easier?:
>>
>> Option A:
>> ---------
>> try
>> {
>>      new Socket(30587);
>> }
>> catch(SocketException e)
>> {
>>      printf("Could not open socket\n");
>> }
>>
>> Option B:
>> ---------
>> try
>> {
>>     new Socket(30587);
>> }
>> catch(Exception e)
>> {
>>     if(e.type == ExceptionType.Socket)
>>         printf("Could not open socket\n");
>>     else
>>         throw e;
>> }
> 
> I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.
> 
> That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.
> 
> 
> Andrei

Just an ill-considered idea from someone who has no idea whether it is even technically possible:

try
{
    new Socket(30587);
}
catch(Exception!(SocketConnect) e)
{
    printf("Could not open socket\n");
}
catch(Exception ex)
{
    printf("Serious trouble--I probably shouldn't have caught this at all!\n");
}

Granted, you'd still need to define SocketConnect and its ilk, but since they'd be simple (and in some cases  empty) data containers they would be quick and easy to define, especially since you wouldn't need to implement all of Exception's constructors.

Of course, this is back to catching by type again, so maybe it's not a good idea even if it is possible.
October 23, 2008
David Gileadi wrote:
> Andrei Alexandrescu wrote:
>> Robert Fraser wrote:
>>> Andrei Alexandrescu wrote:
>>>> Jarrett Billingsley wrote:
>>>>> On Wed, Oct 22, 2008 at 6:49 AM, Jacob Carlborg <doobnet@gmail.com> wrote:
>>>>>> I think the name ArrayBoundsException should be changed to a more general
>>>>>> name like BoundsException, OutOfBoundsException or
>>>>>> IndexOutOfBoundsException. Then you can use the exception in every class
>>>>>> that have some sort of index operation and not just for an array/array
>>>>>> class.
>>>>>>
>>>>>
>>>>> 2nded.
>>>>
>>>> I agree. In fact I wanted to ask you all the following question. What do
>>>> you think about the current exception hierarchy in phobos? I think it is
>>>> terrible. Each module in std you open, the first piece of code to be
>>>> seen is the "class ThisModuleNameException" definition. In many (most?)
>>>> cases the module-specific exception does absolutely nothing in addition
>>>> to its base class. The putative reader (including me) tends to scroll non-critically over that passage without even blinking, mumbling in a trance - of course, yes, each module should define at least one exception type.
>>>>
>>>> Until one day when you stop scrolling and say, wait a minute. This all is repetition. And there are alternatives to catching by type - you can catch the base type and consult a field. And in fact I don't remember seeing code that depends on exceptions thrown from different modules having different types. There's something wrong here!
>>>>
>>>> I think most exception classes in phobos should be yanked if it's possible for their functionality (often nil) to be moved in the Exception base class. The module name should be a member. If someone needs to deal with an exception thrown from a specific module, they can always inspect the field. We don't need a huge hierarchy for that.
>>>>
>>>>
>>>> Andrei
>>>
>>> Yes, you _could_ use a field... but the "catch a subclass" style is already there and is supported by the language, so _why_ use a field? Which of the following is easier?:
>>>
>>> Option A:
>>> ---------
>>> try
>>> {
>>>      new Socket(30587);
>>> }
>>> catch(SocketException e)
>>> {
>>>      printf("Could not open socket\n");
>>> }
>>>
>>> Option B:
>>> ---------
>>> try
>>> {
>>>     new Socket(30587);
>>> }
>>> catch(Exception e)
>>> {
>>>     if(e.type == ExceptionType.Socket)
>>>         printf("Could not open socket\n");
>>>     else
>>>         throw e;
>>> }
>>
>> I think you'd be hard-pressed to justify the "if" inside the second example. You couldn't create a Socket, period. It doesn't matter where exactly the exception was generated from.
>>
>> That's one thing about large exception hierarchies: everybody can come with cute examples on how they could be useful. As soon as the rubber hits the road, however, differentiating exceptions by type becomes useless.
>>
>>
>> Andrei
> 
> Just an ill-considered idea from someone who has no idea whether it is even technically possible:
> 
> try
> {
>     new Socket(30587);
> }
> catch(Exception!(SocketConnect) e)
> {
>     printf("Could not open socket\n");
> }
> catch(Exception ex)
> {
>     printf("Serious trouble--I probably shouldn't have caught this at all!\n");
> }
> 
> Granted, you'd still need to define SocketConnect and its ilk, but since they'd be simple (and in some cases  empty) data containers they would be quick and easy to define, especially since you wouldn't need to implement all of Exception's constructors.
> 
> Of course, this is back to catching by type again, so maybe it's not a good idea even if it is possible.

It's very possible. Parameterizing Exception the same way Tuple is parameterized (with e.g. module name and name/value type pairs) is a great idea!

Andrei
October 23, 2008
On Thu, 23 Oct 2008 17:47:52 +0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Denis Koroskin wrote:
>> On Thu, 23 Oct 2008 04:02:22 +0400, Sean Kelly <sean@invisibleduck.org> wrote:
>>
>>> Jarrett Billingsley wrote:
>>>> On Wed, Oct 22, 2008 at 7:13 PM, Sean Kelly <sean@invisibleduck.org> wrote:
>>>>> Errors represent situations which are typically non-recoverable--program
>>>>> logic errors, for example, or situations where data corruption may have
>>>>> occurred--while Exceptions represent the bulk of normal execution errors,
>>>>> including OutOfMemory conditions.
>>>>  How, pray tell, is an app supposed to recover from an out-of-memory condition?
>>>
>>> By releasing dynamically allocated memory.  I'd expect some to be released automatically as the stack is unrolled to the catch point anyway.  For example:
>>>
>>> void main()
>>> {
>>>      try { fn(); }
>>>      catch( Exception e ) {}
>>>      int[] x = new int[16384];
>>> }
>>>
>>> void fn()
>>> {
>>>      int[] x = new int[16384];
>>>      fn();
>>> }
>>>
>>> Eventually this app will run out of memory (hopefully before it runs out of stack space) and an OutOfMemoryException will be thrown.  As the stack is unwound, all valid references to this memory will be released.   So the allocation in main() should trigger a collection which frees up all the now-unreferenced memory, thus allowing the allocation in main() to succeed.
>>>
>>> For manual recovery, consider an app that does a great deal of internal caching.  On an OutOfMemory condition the app could clear its caches and   then retry the operation.  This is probably a bad example, but I think the general idea of trapping and recovering from such a state is potentially valid.
>>>
>>>
>>> Sean
>>  I think that OutOfMemoryException should *not* be recoverable. Instead, language should provide some hookable callback (like onOutOfMemoryError()) which is called when memory limit is reached so that program may free some unused memory (which is held by user since it is not garbage-collected) and tries to allocate the memory again without failure (return true from callback). User might decide to re-throw some other kind of *exception*, like NotEnoughMemoryException(), to catch and recover, or pass it (return false from callback) which in turn will finally throw OutOfMemoryError().
>
> But one of the best things possible to do is unwind the stack and fall back to a higher position with less state. A function cannot do that, an exception can.

You can do that, of course, just don't handle the onOutOfMemory custom callback (per my proposal).

> Why do you guys want to avoid exceptions in one of the few cases when they are exactly, but exactly what the doctor prescribed?
>
> Andrei

My concern is to avoid program flow interrupt and fall-back to some recovery code if possible. One of such cases is OutOfMemoryException.

For example, I recieve some network message. An object is constructed and about to be inserted into the message list. Imagine that an OutOfMemoryException is thrown during the insertion. It is quite hard (if possible) and not generally disirable to revert network state: I have to emulate that message is not recieved yet so that I get it later (at a second attempt, after OutOfMemory exception is processed and some memory freed), etc. Besides, where would you put the catch(OutOfMemoryError) code? Far from the exception source point, most likely, which is even worse for recovery.

The solution I would prefer is to avoid that situation at all! No memory? Fine, I'll clean this memory pool and that one, too. Try again, please! Still no memory? Then re-throw the exception so that upper forces handle the situation.

I don't say that exception recovery is not needed - of course it is! - but I prefer to avoid it if possible. It is just safer.

Leave the user unaware that an out of memory error has been occured and recovered from is my best wish.