February 21, 2012
On 2012-02-20 23:44, Juan Manuel Cabo wrote:
>> I still don't like the idea of using Variant[string], though.
>>
>> (1) It doesn't allow compile-time type checking. This is a big minus, in
>> my book.
>
> When you need compile-time type checking, define a variable in your class.
> Just make sure that you are creating that new exception class for a good reason.
>
> When a user needs to add a variable to the exception, he can add it
> without putting your exception class chained in a new type of exception,
> that will hide your class from being selected by upstream catch blocks
> in the call tree.
>
>>
>> (2) It's overly flexible. Anyone along the call stack can insert
>> (hopefully NOT delete!!) additional data into the Exception object, as
>> the stack is unwound.
>
> As is currently the case.
> Did you know that anyone can overwrite any field of the exception and
> rethrow it? Such as msg field and so on?

No one says the fields need to be public instance variables. You could take the arguments in the constructor and only have getters.

-- 
/Jacob Carlborg
February 21, 2012
On Mon, 20 Feb 2012 21:08:07 -0600, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:

> On Mon, Feb 20, 2012 at 08:41:47PM -0600, Andrei Alexandrescu wrote:
>> On 2/20/12 8:33 PM, Robert Jacques wrote:
>> >Variant e = new MyException();
>> >writeln( e.filename, e.line, e.column);
>> >
>> >Aren't __traits and opDispatch fun?
>>
>> I thought the same, but was afraid to bring the nuclear bomb out :o).
> [...]
>
> Mmm... but *this* nuclear bomb, I like. :-)
>
>
> T

It gets better, you can also create prototype objects:

    Variant p1 = Tuple!(real,"x",real,"y")(1,2);

    Variant p2;                        // An unitialized Variants attemps to convert
    p2.x = 1;                          // itself to Variant[string] on assignment
    p2.__reflect("y",Variant(2) );
    p2["opBinary!+"]  = (Variant a, real b   ){return b;  }; // Create an 'operator'
    p2["opBinary!+"] ~= (Variant a, Variant b){return a.x;}; // overload set

    assert( (p2 + 4) == 4 && (p2 + p1) == 1);
February 21, 2012
On 2/21/12 4:48 AM, foobar wrote:
> On Tuesday, 21 February 2012 at 02:23:58 UTC, Andrei Alexandrescu wrote:
>> On 2/20/12 7:02 PM, Juan Manuel Cabo wrote:
>>> oops, sorry!! I just saw a post by someone named Jose. My thousand
>>> apollogies!!
>>
>> I got confused. It was your argument I meant to refer to - adding info
>> to the exception in flight.
>>
>> Andrei
>
> I'd implement this along these lines:
>
> class WithErrorCode(E) : E {
> int errorCode;
> this(Args)(int err, Args args) { this.errorCode = err; super(args); }
> }
>
> and add this wrapper where relevant. e.g. replace:
> throw new FileNotFoundException(...);
> with something like:
> throw new WithErrorCode!FileNotFoundException(-1, ...);
>
> This is a localized change that doesn't affect all uses of exceptions
> and it remains type-safe. surely this is a better solution than the hash
> table?

The two approaches don't compete as one is static and the other is dynamic. For example, how does one add contextual information "While opening table in {database}" to the current exception of type WithErrorCode!FileNotFoundException?


Andrei
February 21, 2012
On 2/21/12 4:34 AM, deadalnix wrote:
> Le 20/02/2012 21:57, Andrei Alexandrescu a écrit :
>> class DRoxException : Exception
>> {
>> mixin(enableRTTI);
>> ... normal implementation ...
>> }
>>
>>
>> Andrei
>>
>>
>
> Why not using std.rtti and generate run time reflection info from
> compile time reflexion capability ?
>
> This would enable that feature without writing it the language, so it
> would prevent to bloat the binary when size matter and reflexion isn't
> needed.

Yes, that's the idea.

Andrei
February 21, 2012
On 2/21/12 4:40 AM, Vincent wrote:
> On Saturday, 18 February 2012 at 18:52:05 UTC, Andrei Alexandrescu wrote:
>> From experience I humbly submit that catching by type is most of the
>> time useless.
>
> Completely disagree. Types allow to control place for "catch". Say, some
> deeply nested function catches its own exceptions, while outer function
> catches the rest - exceptions higher in hierarchy. But to have benefit
> you have to create exceptions hierarchy - this is the main point.

As the next hundreds of messages discuss, matters are not all that simple :o).

Andrei
February 21, 2012
On 2/21/12 5:11 AM, Jacob Carlborg wrote:
> On 2012-02-21 03:34, Andrei Alexandrescu wrote:
> I think the correct way of handling this is provide enough information
> in the exception so a message can be built where the exception is
> caught.

Quite so. I'd add "using a unified interface so reusable code can work with heterogeneous concrete exceptions".

> It might happen the you want to catch the same exception in
> different parts of the code and build different messages.

Yah, that's different string tables.


Andrei


February 21, 2012
On 2/21/12 5:55 AM, Regan Heath wrote:
> On Sun, 19 Feb 2012 23:04:59 -0000, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 2/19/12 4:00 PM, Nick Sabalausky wrote:
>
>>> Seriously, how is this not *already* crystal-clear? I feel as if
>>> every few
>>> weeks you're just coming up with deliberately random shit to argue so
>>> the
>>> rest of us have to waste our time spelling out the obvious in insanely
>>> pedantic detail.
>>
>> It sometimes happened to me to be reach the hypothesis that my
>> interlocutor must be some idiot. Most often I was missing something.
>
> I get the impression that you find "Devil's advocate" a useful tool for
> generating debate and out of the box thinking.. there is something to be
> said for that, but it's probably less annoying to some if you're clear
> about that from the beginning. :p

Where did it seem I was playing devil's advocate? Thanks.

Andrei


February 21, 2012
On 2/21/12 6:36 AM, Jacob Carlborg wrote:
> On 2012-02-20 23:44, Juan Manuel Cabo wrote:
>>> I still don't like the idea of using Variant[string], though.
>>>
>>> (1) It doesn't allow compile-time type checking. This is a big minus, in
>>> my book.
>>
>> When you need compile-time type checking, define a variable in your
>> class.
>> Just make sure that you are creating that new exception class for a
>> good reason.
>>
>> When a user needs to add a variable to the exception, he can add it
>> without putting your exception class chained in a new type of exception,
>> that will hide your class from being selected by upstream catch blocks
>> in the call tree.
>>
>>>
>>> (2) It's overly flexible. Anyone along the call stack can insert
>>> (hopefully NOT delete!!) additional data into the Exception object, as
>>> the stack is unwound.
>>
>> As is currently the case.
>> Did you know that anyone can overwrite any field of the exception and
>> rethrow it? Such as msg field and so on?
>
> No one says the fields need to be public instance variables. You could
> take the arguments in the constructor and only have getters.

I think he meant to say things have been like that for a while and there's no blood in the streets.

Andrei


February 21, 2012
On Tuesday, 21 February 2012 at 14:13:55 UTC, Andrei Alexandrescu wrote:
> On 2/21/12 4:48 AM, foobar wrote:
>> On Tuesday, 21 February 2012 at 02:23:58 UTC, Andrei Alexandrescu wrote:
>>> On 2/20/12 7:02 PM, Juan Manuel Cabo wrote:
>>>> oops, sorry!! I just saw a post by someone named Jose. My thousand
>>>> apollogies!!
>>>
>>> I got confused. It was your argument I meant to refer to - adding info
>>> to the exception in flight.
>>>
>>> Andrei
>>
>> I'd implement this along these lines:
>>
>> class WithErrorCode(E) : E {
>> int errorCode;
>> this(Args)(int err, Args args) { this.errorCode = err; super(args); }
>> }
>>
>> and add this wrapper where relevant. e.g. replace:
>> throw new FileNotFoundException(...);
>> with something like:
>> throw new WithErrorCode!FileNotFoundException(-1, ...);
>>
>> This is a localized change that doesn't affect all uses of exceptions
>> and it remains type-safe. surely this is a better solution than the hash
>> table?
>
> The two approaches don't compete as one is static and the other is dynamic. For example, how does one add contextual information "While opening table in {database}" to the current exception of type WithErrorCode!FileNotFoundException?
>
>
> Andrei

This works:
// note: the int parameter above isn't static
dbConn.query("select age from people where id='foobar'");
throw new WithErrorCode!FileNotFoundException(
          db.rs.getValue(1), "file not found");

This approach fails if you don't know ahead of time what *fields* you want to add to your exception but I'd argue that this is unrealistic. An exception is thrown as a response to a specific erroneous condition which means you already know *what* the problem is and what kind of data is needed to describe it.

Can you offer a real world use-case where the above isn't sufficient?
February 21, 2012
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. You made some good points there, and had an interesting idea, so I'll just comment on that.

> 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.

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?

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?

artur