February 19, 2012
On 2/19/12 6:35 AM, deadalnix wrote:
> Le 19/02/2012 09:02, Andrei Alexandrescu a écrit :
>>>> I'm thinking an error is transient if retrying the operation with the
>>>> same exact data may succeed. That's a definition that's simple,
>>>> useful, and easy to operate with.
>>> [...]
>>>
>>> But if that's the case, what's the use of an exception at all?
>>
>> Centralization.
>>
>> Andrei
>>
>
> Please stop answering like that. From the begining of this topic
> Jonathan M Davis, H. S. Teah (and myself ?) raised very valid points.
> What do you expect from that discussion if yourself you do not put any
> arguement on the table ?

The answer is meaningful. The purpose of exceptions is allowing for centralized error handling, and a capabilities-based system makes that simple (e.g. you get to make decisions about recoverability in one place, regardless of which part of the exception hierarchy the exception originated.

Andrei
February 19, 2012
On 2/19/12 6:48 AM, Jacob Carlborg wrote:
> On 2012-02-19 01:09, Andrei Alexandrescu wrote:
>> On 2/18/12 6:03 PM, Jonathan M Davis wrote:
>>> On Saturday, February 18, 2012 17:30:10 Andrei Alexandrescu wrote:
>>>> The alternative is with virtuals. Do you see a lot of virtuals in base
>>>> exceptions? Do you see dramatically different interface for different
>>>> exception types?
>>>
>>> You mean virtual functions? The problem is that each exception type
>>> could have
>>> information specific to _it_ which makes no sense in a base class. For
>>> instance, Exception does to have errno in it. FileException does.
>>>
>>> If we have GetOptException, it should have a variable for which flag
>>> failed.
>>> Exception doesn't have that. And what if the flag failed because it
>>> was given a
>>> bad argument? Then the exception needs a field for that argument. Then
>>> you can
>>> get something like
>>>
>>> try
>>> getopt(args, ...)
>>> catch(MissingArgumentException mae)
>>> {
>>> stderr.writefln("%s is missing an argument", mae.flag);
>>> return -1;
>>> }
>>> catch(InvalidArgumentException iae)
>>> {
>>> stderr.writelfln("%s is not a valid argument for %s. You must give it a
>>> %s.", mae.arg, mae.flag, mae.expectedType);
>>> return -1;
>>> }
>>> catch(UnknownFlagException ufe)
>>> {
>>> stderr.writefln("%s is not a known flag.", ufe.ufe);
>>> return -1;
>>> }
>>> catch(GetOptException goe)
>>> {
>>> stderr.writefln("There was an error with %s", goe.flag);
>>> return -1;
>>> }
>>> //A delegate that you passed to getopt threw an exception.
>>> catch(YourException ye)
>>> {
>>> //...
>>> }
>>> catch(Exception e)
>>> {
>>> stderr.writeln("An unexpected error occured.");
>>> return -1;
>>> }
>>>
>>> You can't do _anything_ like that right now.
>>
>> Of course I can. They call it toString(). The code above pretty much
>> proves my point with so much wonderful irony.
>>
>> Andrei
>>
>
> If you actually want to try and recover from the error rather than just
> simply print the error message, toString is out of the window.

Absolutely. I was answering to the example given.

Andrei


February 19, 2012
On 2/19/12 6:49 AM, Jacob Carlborg wrote:
> On 2012-02-19 01:09, Andrei Alexandrescu wrote:
>> On 2/18/12 6:03 PM, Jonathan M Davis wrote:
>>> On Saturday, February 18, 2012 17:30:10 Andrei Alexandrescu wrote:
>>>> The alternative is with virtuals. Do you see a lot of virtuals in base
>>>> exceptions? Do you see dramatically different interface for different
>>>> exception types?
>>>
>>> You mean virtual functions? The problem is that each exception type
>>> could have
>>> information specific to _it_ which makes no sense in a base class. For
>>> instance, Exception does to have errno in it. FileException does.
>>>
>>> If we have GetOptException, it should have a variable for which flag
>>> failed.
>>> Exception doesn't have that. And what if the flag failed because it
>>> was given a
>>> bad argument? Then the exception needs a field for that argument. Then
>>> you can
>>> get something like
>>>
>>> try
>>> getopt(args, ...)
>>> catch(MissingArgumentException mae)
>>> {
>>> stderr.writefln("%s is missing an argument", mae.flag);
>>> return -1;
>>> }
>>> catch(InvalidArgumentException iae)
>>> {
>>> stderr.writelfln("%s is not a valid argument for %s. You must give it a
>>> %s.", mae.arg, mae.flag, mae.expectedType);
>>> return -1;
>>> }
>>> catch(UnknownFlagException ufe)
>>> {
>>> stderr.writefln("%s is not a known flag.", ufe.ufe);
>>> return -1;
>>> }
>>> catch(GetOptException goe)
>>> {
>>> stderr.writefln("There was an error with %s", goe.flag);
>>> return -1;
>>> }
>>> //A delegate that you passed to getopt threw an exception.
>>> catch(YourException ye)
>>> {
>>> //...
>>> }
>>> catch(Exception e)
>>> {
>>> stderr.writeln("An unexpected error occured.");
>>> return -1;
>>> }
>>>
>>> You can't do _anything_ like that right now.
>>
>> Of course I can. They call it toString(). The code above pretty much
>> proves my point with so much wonderful irony.
>>
>> Andrei
>>
>
> Say you want to do something like what Git does:
>
> $ git statu
> git: 'statu' is not a git command. See 'git --help'.
>
> Did you mean this?
> status
> $
>
> That would be quite hard with just toString.

An exception hierarchy won't make that easier or more modular. What's needed there is the command and a table of all commands.

Andrei
February 19, 2012
On 2/19/12 7:31 AM, Timon Gehr wrote:
> On 02/19/2012 09:26 AM, H. S. Teoh wrote:
>> On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
>> [...]
>>> So, while at first glance, it seems like a good idea, I think that it
>>> has too many issues as-is to work. It might be possible to adjust the
>>> idea to make it workable though. Right now, it's possible to do it via
>>> mixins or calling a function inside the catch, but doing something
>>> similar to this would certainly be nice, assuming that we could sort
>>> out the kinks.
>> [...]
>>
>> I have an idea. What about "signature constraints" for catch, ala
>> template signature constraints? Something like this:
>>
>> try {
>> ...
>> } catch(IOException e)
>> if (e.errno in subsetYouWantToHandle)
>> {
>> ...
>> }
>>
>> Just using IOException as an example. The idea is to allow arbitrary
>> expressions in the constraint so whatever doesn't satisfy the constraint
>> will be regarded as "not caught", even if the base type matches.
>>
>>
>> T
>>
>
> Nice.

That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today:

try {
  ...
} catch(IOException e)
{
  if (e.errno !in subsetYouWantToHandle) throw e;
  ...
}


Andrei
February 19, 2012
On 19-02-2012 15:41, Andrei Alexandrescu wrote:
> On 2/19/12 7:31 AM, Timon Gehr wrote:
>> On 02/19/2012 09:26 AM, H. S. Teoh wrote:
>>> On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote:
>>> [...]
>>>> So, while at first glance, it seems like a good idea, I think that it
>>>> has too many issues as-is to work. It might be possible to adjust the
>>>> idea to make it workable though. Right now, it's possible to do it via
>>>> mixins or calling a function inside the catch, but doing something
>>>> similar to this would certainly be nice, assuming that we could sort
>>>> out the kinks.
>>> [...]
>>>
>>> I have an idea. What about "signature constraints" for catch, ala
>>> template signature constraints? Something like this:
>>>
>>> try {
>>> ...
>>> } catch(IOException e)
>>> if (e.errno in subsetYouWantToHandle)
>>> {
>>> ...
>>> }
>>>
>>> Just using IOException as an example. The idea is to allow arbitrary
>>> expressions in the constraint so whatever doesn't satisfy the constraint
>>> will be regarded as "not caught", even if the base type matches.
>>>
>>>
>>> T
>>>
>>
>> Nice.
>
> That helps. This quite nicely illustrates that types don't necessarily
> need to proliferate. Something not much more constraining can be done
> today:
>
> try {
> ...
> } catch(IOException e)
> {
> if (e.errno !in subsetYouWantToHandle) throw e;
> ...
> }
>
>
> Andrei

As I pointed out on the pull request, this is *evil*. It resets the stack trace.

-- 
- Alex
February 19, 2012
On 2/19/12 8:44 AM, Alex Rønne Petersen wrote:
> On 19-02-2012 15:41, Andrei Alexandrescu wrote:
>> That helps. This quite nicely illustrates that types don't necessarily
>> need to proliferate. Something not much more constraining can be done
>> today:
>>
>> try {
>> ...
>> } catch(IOException e)
>> {
>> if (e.errno !in subsetYouWantToHandle) throw e;
>> ...
>> }
>>
>>
>> Andrei
>
> As I pointed out on the pull request, this is *evil*. It resets the
> stack trace.

I understand, that's a good point. Could the matter be considered an implementation issue?

Andrei
February 19, 2012
On 19-02-2012 15:46, Andrei Alexandrescu wrote:
> On 2/19/12 8:44 AM, Alex Rønne Petersen wrote:
>> On 19-02-2012 15:41, Andrei Alexandrescu wrote:
>>> That helps. This quite nicely illustrates that types don't necessarily
>>> need to proliferate. Something not much more constraining can be done
>>> today:
>>>
>>> try {
>>> ...
>>> } catch(IOException e)
>>> {
>>> if (e.errno !in subsetYouWantToHandle) throw e;
>>> ...
>>> }
>>>
>>>
>>> Andrei
>>
>> As I pointed out on the pull request, this is *evil*. It resets the
>> stack trace.
>
> I understand, that's a good point. Could the matter be considered an
> implementation issue?
>
> Andrei

Unfortunately, I don't think so. As far as I am aware, there is no good way to solve the problem; you can't know whether the user intended to rethrow (and thus continue the stack trace) as opposed to doing a clean throw.

You could say that if the throw statement is "throw new T();", then a new stack trace is created, and in all other cases, the stack trace is continued, however, this doesn't work universally (think: creating and returning an exception object in a function and then throwing it).

-- 
- Alex
February 19, 2012
On 2012-02-19 13:27, Juan Manuel Cabo wrote:
> How about adding a string[string] or a variant[string] to the Exception
> class, so one can know details about the subclassed exception without
> downcasting? How ugly would that be?
>
> For instance:
>
> ...
> catch (Exception ex) {
> if ("transient" in ex.details) {
> repeatOneMoreTime();
> }
> if ("i18n_code" in ex.details) {
> log(translate(ex.details["i18n_code"]));
> }
> }
> ...
>
> Details can be standard by convention or otherwise custom.
> (I can see that this can lead to messy proliferation of details, but at
> least solves most of the issues).

How would you know which keys are available in "ex.details", documentation?

-- 
/Jacob Carlborg
February 19, 2012
On Sun, Feb 19, 2012 at 12:44 PM, Alex Rønne Petersen <xtzgzorex@gmail.com> wrote:
> On 19-02-2012 15:41, Andrei Alexandrescu wrote:
>>
>> On 2/19/12 7:31 AM, Timon Gehr wrote:
>>>
>>> On 02/19/2012 09:26 AM, H. S. Teoh wrote:
>>>>
>>>> On Sat, Feb 18, 2012 at 11:52:00PM -0800, Jonathan M Davis wrote: [...]
>>>>>
>>>>> So, while at first glance, it seems like a good idea, I think that it has too many issues as-is to work. It might be possible to adjust the idea to make it workable though. Right now, it's possible to do it via mixins or calling a function inside the catch, but doing something similar to this would certainly be nice, assuming that we could sort out the kinks.
>>>>
>>>> [...]
>>>>
>>>> I have an idea. What about "signature constraints" for catch, ala template signature constraints? Something like this:
>>>>
>>>> try {
>>>> ...
>>>> } catch(IOException e)
>>>> if (e.errno in subsetYouWantToHandle)
>>>> {
>>>> ...
>>>> }
>>>>
>>>> Just using IOException as an example. The idea is to allow arbitrary expressions in the constraint so whatever doesn't satisfy the constraint will be regarded as "not caught", even if the base type matches.
>>>>
>>>>
>>>> T
>>>>
>>>
>>> Nice.
>>
>>
>> That helps. This quite nicely illustrates that types don't necessarily need to proliferate. Something not much more constraining can be done today:
>>
>> try {
>> ...
>> } catch(IOException e)
>> {
>> if (e.errno !in subsetYouWantToHandle) throw e;
>> ...
>> }
>>
>>
>> Andrei
>
>
> As I pointed out on the pull request, this is *evil*. It resets the stack trace.
>

What? Is there a technical reason why throw resets the stack? Java doesn't work this way. In java the stack is created when the object Throwable is created:

"A throwable contains a snapshot of the execution stack of its thread at the time it was created. It can also contain a message string that gives more information about the error. Finally, it can contain a cause: another throwable that caused this throwable to get thrown. The cause facility is new in release 1.4. It is also known as the chained exception facility, as the cause can, itself, have a cause, and so on, leading to a "chain" of exceptions, each caused by another. "

We should consider changing this to work more like Java. This allows for patterns like:

// Log the stack but don't throw
auto e = new Exception();
writefln(e.stack);

Thanks,
-Jose

> --
> - Alex
February 19, 2012
On 2012-02-19 10:26, Jonathan M Davis wrote:
> On Sunday, February 19, 2012 19:00:20 Daniel Murphy wrote:
>> I wasn't really serious about implicit fallthrough.
>
> Lately, it seems like I can never tell whether anyone's being serious or not
> online. :)
>
>> Out of the syntaxes I could come up with:
>> catch(Ex1, Ex2 e)
>> catch(e : Ex1, Ex2)
>> catch(Ex1 | Ex2 e) // java 7 syntax, horrible
>>
>> I like (e : list) the best.  Naturally it would also accept a type tuple of
>> exceptions.
>>
>> http://d.puremagic.com/issues/show_bug.cgi?id=7540
>
> LOL. Personally, I actually think that the Java 7 syntax looks great (I'd
> never seen it before), but catch(e : Ex1, Ex2) is just as good and more
> consistent with the language as a whole, since it doesn't try to give any
> operators a new meaning (as Java's does).
>
> - Jonathan M Davis

How is "catch(e : Ex1, Ex2)" consistent with the language? It's completely backwards. catch-block are written as follows:

catch (Exception e) {}

Not

catch (e : Exception) {}

-- 
/Jacob Carlborg