View mode: basic / threaded / horizontal-split · Log in · Help
February 21, 2012
Re: The Right Approach to Exceptions
On Tuesday, 21 February 2012 at 14:56:52 UTC, Andrei Alexandrescu 
wrote:
>>
>> Can you offer a real world use-case where the above isn't 
>> sufficient?
>
> This has been discussed. A function would want to add 
> contextual information to an exception and rethrow it. 
> Requiring a new type for each such flow does not scale.
>
>
> Andrei

This solution works given the fact that I know what type of data 
I want to add and I can't see a situation where that isn't the 
case. In Juan's case, he wanted to add an error code so he _knew_ 
already what type is required (int).
It seems we did a full circle. I'll ask again, what are you 
trying to optimize here? Number of instantiations?
February 21, 2012
Re: The Right Approach to Exceptions
On Tuesday, 21 February 2012 at 16:15:17 UTC, Juan Manuel Cabo 
wrote:
>> FileNotFoundException is the super class of the others so the 
>> first catch clause is enough. in fact, the others will
>> never be called if listed in the above order.
>
> Nice! I missed that. But what if you want to add ErrorCode and 
> Rainbows?
> And with your approach, one has to test for type and downcast, 
> or
> otherwise have multiple catch blocks (I don't want to miss plain
> FileNotFoundExceptions). So it's square one.
>
> With Variant[string] (or something equivalent, nothing better 
> comes to mind)
> one does:
>
>
>     try {
>         ...
>     } catch (FileNotFoundException ex) {
>          if (ex.hasInfo(MyNameConstant)) {
>              ... use that ...
>          }
>          ... common handling ...
>     }
>
>
> --jm

Regarding the downcast - you still perform a check in the code 
above! You gained nothing by replacing a type check with a check 
on a hash.

Regarding composition of several traits - even that simple 
snippet is enough:
throw new 
WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);

That's without further design which could probably improve this 
further.
February 21, 2012
Re: The Right Approach to Exceptions
On 2/21/12 10:39 AM, foobar wrote:
> Regarding the downcast - you still perform a check in the code above!
>  You gained nothing by replacing a type check with a check on a
> hash.

You do gain because capability checks don't force a tree structure, 
whereas downcasting does.

> Regarding composition of several traits - even that simple snippet is
>  enough: throw new
> WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);
>
> That's without further design which could probably improve this
> further.

To quote a classic:

> It's clear that you are trying to generify exceptions. This
> contradicts the very notion of what exceptions are. You also seem to
> try to optimize the amount of exception classes. Making user code
> convoluted for the sake of some premature optimization which most
> likely has negligible affect is completely unacceptable. I get that
> you are a templates master, that does *NOT* mean everything must be
> made generic. You seem to prove the old saying that when all you have
> is a hammer everything looks like a nail.

It's gotta be one or the other.


Andrei
February 21, 2012
Re: The Right Approach to Exceptions
> throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);

So:

   catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) {
        ....
   } catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) {
        ....
   } catch (WithErrorCode!withRainbows!withFoobar!FileNotFoundException ex) {
        ....
   } catch (WithRainbows!withFoobar!withErrorCode!FileNotFoundException ex) {

and so on (in this case will be, its 3! == 6).

and you would have to write them all. You cannot catch only WithRainbows!* because
you miss the FileNotFoundException at the end.


Please, refer to my previous posts.
I don't want to start to repaste my posts.
In one of them, I said that what you care about for the catch selection
is the *what* of the error.   Not the *cause* of the error, not the *where*
of the error (no one catches by *where*). And that it seems wrong to encode
anything other than the *what* of the error in the type name. Other things
such as the cause or the date should be encoded inside the exception object
instead of in the exception class type name.

I thought that an alternative to Variant[string] would be to have some virtual
functions overrideable (getExceptionData(string dataName) or something).
but they would all have to return Object or Variant, so it's the same thing.

--jm


On 02/21/2012 01:39 PM, foobar wrote:
> On Tuesday, 21 February 2012 at 16:15:17 UTC, Juan Manuel Cabo wrote:
>>> FileNotFoundException is the super class of the others so the first catch clause is enough. in fact, the others will
>>> never be called if listed in the above order.
>>
>> Nice! I missed that. But what if you want to add ErrorCode and Rainbows?
>> And with your approach, one has to test for type and downcast, or
>> otherwise have multiple catch blocks (I don't want to miss plain
>> FileNotFoundExceptions). So it's square one.
>>
>> With Variant[string] (or something equivalent, nothing better comes to mind)
>> one does:
>>
>>
>>     try {
>>         ...
>>     } catch (FileNotFoundException ex) {
>>          if (ex.hasInfo(MyNameConstant)) {
>>              ... use that ...
>>          }
>>          ... common handling ...
>>     }
>>
>>
>> --jm
> 
> Regarding the downcast - you still perform a check in the code above! You gained nothing by replacing a type check with
> a check on a hash.
> 
> Regarding composition of several traits - even that simple snippet is enough:
> throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);
> 
> That's without further design which could probably improve this further.
February 21, 2012
Re: The Right Approach to Exceptions
Walter Bright wrote:
> On 2/18/2012 3:13 PM, 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.
>
> I think typed exceptions are a good idea, but something looks wrong with
> these.
>
> (Also, having a large number of exception types is going to produce a
> lot of program size bloat. Remember, for EVERY class type, you've got
> the vtbl[], the .init data, and the TypeInfo. Going to town on exception
> types can really add this up.)

I feel that druntime might be optimized.

1) I think most of the bloat comes from .init data. For example this 
program:

class A
{
    ubyte[1024 * 1024 * 10] tenMegabytes;
}

class B : A {}
class C : B {}
class D : C {}

void main() { }

compiles to 40 MB exe under windows. Every class has copy of the same 10 
MB .init data.

I wonder why init is byte[] array. IMHO the better thing would be making 
it byte[][], i.e. array of byte arrays. Then some derived parts may be 
shared as byte array slices. This maybe little slower because it adds 
another level of indirection.

By the way, current byte[] array solution doesn't speed up void 
initializers, because it always overwrites void fields. This simple program:

import std.stdio;
class A { ubyte[8] test = void; }
void main() { writeln((new A()).test); }

always prints [0, 0, 0, 0, 0, 0, 0, 0].

With byte[][] array, some slices may have null ptrs so they may be 
initialized using memset() or not initialized at all (when using void 
initializer).

2) vtbl[] might also be shared for derivation chains that don't override 
any functions. But that may slow down program start, because vtables 
must be constructed at runtime.

Please correct me if I'm wrong :) I think that the use of classes should 
not be limited because of implementation issues. For me it's purely 
implementation issue, and I suppose it may be addressed with more 
optimized ABI or druntime.
February 21, 2012
Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)
On Tue, Feb 21, 2012 at 03:54:30PM +0100, Artur Skawina wrote:
> On 02/21/12 09:15, H. S. Teoh wrote:
> > 
> > Sorry for this super-long post, but I wanted to lay my ideas out in a
> > coherent fashion so that we can discuss its conceptual aspects without
> > getting lost with arguing about the details. I hope this is a step in
> > the right direction toward a better model of exception handling.
> 
> I haven't read all of that huge trolli^Hbrainstorming thread, as most
> of it was just bikeshedding - the exception hierarchy needs to just be
> done; design by committee never works.

It would have helped if you actually read all of it. :) I wasn't talking
about the exception hierarchy at all. I was talking about how to recover
from a given problem P without knowing where in the hierarchy P is, or
even whether there is a hierarchy. Specifically, how high-level code 20
levels up the call stack can do something meaningful with a low-level
error 20 levels down, without having to know anything about error codes,
the context of the error, the state of variables, that sort of thing.
And yet *still* have the low-level details sorted out once a decision is
made.


> You made some good points there, and had an interesting idea, so I'll
> just comment on that. 

Credit where credit is due. This idea is not mine, it's the model they
use in Common Lisp. I don't program in Lisp. I just read the article and
found the idea interesting and possibly very useful in the context of D.

The only thing I added, perhaps, is that instead of problem-specific
conditions, as they appear to have in Lisp, I'm looking at generic
categories of conditions, that you can handle from high-level code
without ever needing to know the specifics of exactly what the condition
is, and yet have the low-level code work it all out once you've made a
decision.


> > The try-catch mechanism is not adequate to implement all the
> > recovery actions described above. As I've said before when
> > discussing what I
> 
> Imagine that catch blocks are delegates. Now, when something throws an
> exception, the catch handler is looked up, just like right now, except
> the stack isn't unwound, the matching catch-delegate is called. If it
> returns 'X' control flow is returned to the 'throw' scope, so that the
> failed operation can be retried.  If it returns 'Y' then  the search
> for another matching catch block continues, that one is called and so
> on. If a delegate returns 'Z' the stack is unwound and control is
> passed to the code following this catch statement.

It's not always as simple as "return control to throw scope", the point
of not rolling back the stack is so that you can present a list of
resolution alternatives to the catch block, and have it select one, then
you proceed with that method of recovery.

These resolution alternatives need to be standardized, since otherwise
you pollute high-level code with low-level knowledge (suboperation Y023
20 levels down the call stack has failure mode F04 which has recovery
options R078, R099, R132, so I can catch F04, then choose between R089,
R099, R132), making it only able to deal with a single problem decided
beforehand by the coder. What you want is to be able to say things like,
given any transient problem, I don't care what exactly, retry 5 times
then give up. Or, given any component failure, I don't know nor care
which component, try an alternative component if one exists, otherwise
give up.

This way, if a new component is added to the low-level code, with brand
new ways of failure, the high-level code can still handle the new
failures, even though when it was written such problems didn't even
exist yet.


> Add a bit syntactic sugar, and the result could be something like
> this:
> 
> void f1(C c) {
>    retry:
>    if (!c.whatever())
>       throw(retry) new WhateverEx("Help! XYZ failed", moreDetails, errorCodesEtc);
> }
> 
> void f2(C c) {
>    try {
>       f1(c);
>    } catch (WhateverEx e) {
>       if (isTransient(e)) {
>          doSomethingAndPray();
>          continue;
>       }
>       if (nothingICanDo(e))
>          throw;
>       if (iCanDealWithItHere(e))
>          break;
>    }
>    /* ... */
> }
> 
> Wouldn't this be enough to handle all of your cases?

Not quite, but close. :) And this is a very nice syntax.


> If exiting the scope maps to 'break' ie means
> unwind-and-continue-from-here, it's even somewhat compatible with the
> current scheme (you cannot jump with goto to inside the catch blocks,
> but that's not a good idea anyway).
> 
> Implementation question: could this be done w/o causing heap
> allocation in most functions containing catch statements?
[...]

I've thought about using mixin templates to do insert labels to retry
blocks and goto's in catch blocks, so no cost is incurred unless an
error actually happens. Not sure if this is a good implementation
method, though.


T

-- 
BREAKFAST.COM halted...Cereal Port Not Responding. -- YHL
February 21, 2012
Re: The Right Approach to Exceptions
On 2/21/12 10:50 AM, Juan Manuel Cabo wrote:
> I thought that an alternative to Variant[string] would be to have some virtual
> functions overrideable (getExceptionData(string dataName) or something).
> but they would all have to return Object or Variant, so it's the same thing.

Exactly. By and large, I think in the fire of the debate too many people 
in this thread have forgotten to apply a simple OO design principle: 
push policy up and implementation down. Any good primitive pushed up the 
exception hierarchy is a huge win, and any design that advocates 
reliance on concrete types is admitting defeat.

Andrei
February 21, 2012
Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)
Le 21/02/2012 17:56, H. S. Teoh a écrit :
> The only thing I added, perhaps, is that instead of problem-specific
> conditions, as they appear to have in Lisp, I'm looking at generic
> categories of conditions, that you can handle from high-level code
> without ever needing to know the specifics of exactly what the condition
> is, and yet have the low-level code work it all out once you've made a
> decision.
>

About this, I did some sketching, and I think LISP guys may have 
outsmarted you.

Let's consider a transiant condition. You can retry or give up and 
throw. But, if you want to retry or not often depend on what went wrong, 
no ?
February 21, 2012
Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)
On Tue, Feb 21, 2012 at 06:01:09PM +0100, deadalnix wrote:
> Le 21/02/2012 17:56, H. S. Teoh a écrit :
> >The only thing I added, perhaps, is that instead of problem-specific
> >conditions, as they appear to have in Lisp, I'm looking at generic
> >categories of conditions, that you can handle from high-level code
> >without ever needing to know the specifics of exactly what the condition
> >is, and yet have the low-level code work it all out once you've made a
> >decision.
> >
> 
> About this, I did some sketching, and I think LISP guys may have
> outsmarted you.
> 
> Let's consider a transiant condition. You can retry or give up and
> throw. But, if you want to retry or not often depend on what went
> wrong, no ?

True, and there's nothing to stop you from digging into the details of
the raised Condition if you want to. I did consider implementing
Conditions as some kind of class hierarchy, so that the generic
categories are at the top, underneath Condition, then actual specific
Conditions can extend them. If your handler knows of a specific
Condition, then you can access any pertinent additional info, and make
decisions based on that.

But what I wanted to know was, can you still do something meaningful
even if you knew nothing beyond the top-level generic categories of
Conditions? That way, your handler will still work with new Conditions
that you've never seen before.


T

-- 
Never trust an operating system you don't have source for! -- Martin Schulze
February 21, 2012
Re: The Right Approach to Exceptions
I didn't know where I last read it, it got stuck in my head. I 
wrote:

> [...] doesn't mean
> that one must turn the advantages into disadvantages and start
> hammering screws because we love hammers.

I forget to be careful with metaphors, I realize that some must
be emotionally loaded and don't really help in debating since
they can be used either way and are astranged from reason.

(Hahaha, nothing more dangerous than landing on a new 
forum/community
without tip toeing, ohh the rush of excitement!!)

--jm


On Tuesday, 21 February 2012 at 16:49:37 UTC, Andrei Alexandrescu 
wrote:
> On 2/21/12 10:39 AM, foobar wrote:

...

> To quote a classic:
>
>> made generic. You seem to prove the old saying that when all 
>> you have
>> is a hammer everything looks like a nail.
>

...

>
> Andrei
43 44 45 46 47 48 49 50 51
Top | Discussion index | About this forum | D home