April 02, 2013
On Tuesday, 2 April 2013 at 11:04:06 UTC, Dmitry Olshansky wrote:
> 02-Apr-2013 14:23, deadalnix пишет:
>> On Monday, 1 April 2013 at 22:46:49 UTC, Ali Çehreli wrote:
> [snip]
>> Not running cleanup code can transform a small issue in a big disaster
>> as running can make the problem worse.
>>
>> I don't think wiring in the language the fact that error don't run the
>> cleanup code is rather dangerous.
>>
>> If I had to propose something, it would be to handle error the same way
>> exception are handled, but propose a callback that is ran before the
>> error is throw, in order to allow for complete program stop based on
>> user logic.
>
> It's exactly what I have in mind as removing the exception handling is something user can't recreate easily. On the other hand "die on first signs of corruption" is as easy as a hook that calls abort before unwind of Error.
>

It is possible to propose as a default a hook that fails everything and can be overriden.

> Time to petition Walter ;)

April 02, 2013
On Monday, 1 April 2013 at 21:39:18 UTC, Dmitry Olshansky wrote:
> 01-Apr-2013 23:46, Lars T. Kyllingstad пишет:
>> On Monday, 1 April 2013 at 18:40:48 UTC, Dmitry Olshansky wrote:
>>> 2. ProcessException is IMHO a system exception. Plus some RTOses
>>> systems may not have processes in the usaul POSIX sense. It needs some
>>> thought.
>>
>> Well, I *don't* think ProcessException is a SystemException. :) And if
>> druntime/Phobos is to be used on OSes that don't have processes, there
>> are other adaptations that have to be made that are probably more
>> fundamental.
>
> Okay. I guess all of this goes to "Embedded D"/"D Lite" kind of spec.
>
> Last thing - why separating ThreadException vs ProcessException and should they have some base class? Just asking to see the rationale behind it that is missing from DIP currently.

Well, I hadn't even considered that the same exception could be used for both.  I see the similarity between processes and threads, but I also think there is a difference in how/when/where you want to deal with errors related to them.  Say you have a function that starts a new process and then spawns a new thread to monitor it, and then the function fails for some reason.  This could be something you'd want to deal with on different levels, as a ProcessException could simply be that you got the wrong executable name, while a ThreadException would usually point to a much deeper issue.


>>> 4. A quiz as an extension of 3 - where a e.g. serial port exceptions
>>> would fit in this hierarchy? Including Parity errors, data overrun etc.
>>> Let's think a bit ahead and pick names wisely.
>>> Maybe turn NetworkException --> Comm(unication)Exception, I dunno.
>>
>> Good question, I dunno either. :)  I agree we should think about it.

I have thought some more about it, and a basic serial comms error should probably be an IOException.  An error in a higher-level serial protocol, on the other hand, would be a NetworkException, and then the name doesn't suck so much.  CommException may still be better though, or maybe ProtocolException.

>>> 8. For IOExcpetion we might consider adding an indication on which
>>> file handle the problem happened and/or if it's closed/invalid/"mostly
>>> fine" that.
>>
>> Which form do you suggest that such an indicator should take?
>
> That's the trick - I hoped somebody will just say "aha!" and add one :)
>
> The internal handle is hard to represent other then ... some platform specific integer value. There goes generality... Other then this there is a potential to stomp on feet of higher-level abstraction on top of that handle.
>
> That last bit makes me reconsider the idea. While I see some potential use for it I suspect it's too niche to fit in the general hierarchy.
>
>> Bear in
>> mind that it should be general enough to cover all, or at least most,
>> kinds of I/O exceptions.
>
> Adding a Kind that states one of:
> out-of-data (read empty file)
> illegalOp (reading closed file, writing read-only file/socket)
> interrupted (operation was canceled by OS, connection forcibly closed, disk ejected etc.)
> hardFault (OS reports hardware failure)

These are good suggestions, and seem general enough.

Lars
April 02, 2013
On Monday, 1 April 2013 at 22:26:39 UTC, Jonathan M Davis wrote:
> On Monday, April 01, 2013 21:33:22 Lars T. Kyllingstad wrote:
>> My problem with subclasses is that they are a rather heavyweight
>> addition to an API. If they bring no substance (such as extra
>> data), I think they are best avoided.
>
> Except that they're extremely valuable when you need to catch them. [...]
>
> try
> {
>  ...
> }
> catch(FileNotFoundException fnfe)
> {
>  //handling specific to missing files
> }
> catch(PermissionsDeniedException pde)
> {
>  //handling specific to lack of permissions
> }
> catch(FileExceptionfe
> {
>  //generic file handling error
> }
> catch(Exception e)
> {
>  //generic error handling
> }
>
> can be very valuable.

Well, personally, I don't think this is much better than a switch statement.

> In general, I'd strongly suggest having subclasses for
> the various "Kind"s in addition to the kind field. That way, you have the
> specific exception types if you want to have separate catch blocks for different
> error types, and you have the kind field if you just want to catch the base
> exception.

Then you'd have two points of maintenance when you wish to add or remove an error category.

> If anything, exceptions are exactly the place where you want to have derived
> classes with next to nothing in them, precisely because it's the type that the
> catch mechanism uses to distinguish them.

catch is *one* mechanism, switch is another.  I propose to use catch for coarse-level handling (plus the cases where exceptions carry extra data), and switch for fine-level handling.

In my experience, most of the time, you don't even bother distinguishing between the finer categories.  If you can't open a file, well, that's that.  Tell the user why and ask them to try another file.  (I realise that this is highly arguable, of course.)

Lars
April 02, 2013
On 2013-04-02 15:02, H. S. Teoh wrote:

> But what type will ex be, inside the catch block?

I was thinking the closest base type of the both exception types. It's basically what one would do if one would call a common function from multiple catch blocks.

-- 
/Jacob Carlborg
April 02, 2013
On 2013-04-02 15:44, Steven Schveighoffer wrote:

> Yes, this could help.  But it's still not great.  One must still store a
> "type identifier" in the ex, or have to deal with casting to figure out
> what type ex is.

You would have the same problem if you used a common function for handling multiple exceptions.

> It also promotes creating a new type for every single catchable situation.
>
> Consider that we could create one exception type that contains an
> 'errno' member, and if we have the ability to run extra checks for
> catching you could do:
>
> catch(ErrnoException ex) if (ex.errno == EBADF || ex.errno == EBADPARAM)
> {
>     ...
> }

Is that so much better than:

catch (ErrnoException ex)
{
    if (ex.errno == EBADF || ex.errno == EBADPARAM)
    { /*handle exception */ }
    else
        throw ex;
}

> But if we must do it with types, we need:
>
> class ErrnoException(uint e) : Exception
> {
>     enum errno = e;
> }
>
> or worse, to make things easier to deal with we have:
>
> class ErrnoException : Exception
> {
>     int errno;
>     this(int errno) { this.errno = errno; }
> }
>
> class ErrnoExceptionT(uint e) : ErrnoException
> {
>     this() { super(e); }
> }
>
> which would be easier to deal with, but damn what a waste!  Either way,
> every time you catch another errno exception, we are talking about
> instantiating another type.

Does that matter? It still need to create a new instance for every exception thrown. Or are you planning on changing the "errno" field and rethrow the exception?

-- 
/Jacob Carlborg
April 02, 2013
On 2013-04-02 17:56, Lars T. Kyllingstad wrote:

> In my experience, most of the time, you don't even bother distinguishing
> between the finer categories.  If you can't open a file, well, that's
> that.  Tell the user why and ask them to try another file.  (I realise
> that this is highly arguable, of course.)

I would say that there's a big difference if a file exist or if you don't have permission to access it. Think of the command line, you can easily misspell a filename, or forget to use "sudo".

-- 
/Jacob Carlborg
April 02, 2013
On Tue, Apr 02, 2013 at 05:47:30PM +0200, Lars T. Kyllingstad wrote: [...]
> I have thought some more about it, and a basic serial comms error should probably be an IOException.  An error in a higher-level serial protocol, on the other hand, would be a NetworkException, and then the name doesn't suck so much.  CommException may still be better though, or maybe ProtocolException.
[...]

ProtocolException sounds like a low-level TCP or IP exception. I think NetworkException is still the best name, not overly specific, not overly generic. CommException sounds a bit too vague to me.


T

-- 
What are you when you run out of Monet? Baroque.
April 02, 2013
02-Apr-2013 20:39, H. S. Teoh пишет:
> On Tue, Apr 02, 2013 at 05:47:30PM +0200, Lars T. Kyllingstad wrote:
> [...]
>> I have thought some more about it, and a basic serial comms error
>> should probably be an IOException.  An error in a higher-level serial
>> protocol, on the other hand, would be a NetworkException, and then the
>> name doesn't suck so much.  CommException may still be better though,
>> or maybe ProtocolException.
> [...]
>
> ProtocolException sounds like a low-level TCP or IP exception. I think
> NetworkException is still the best name, not overly specific, not overly
> generic. CommException sounds a bit too vague to me.
>

Yeah, I've come to conclusion that NetworkException beats CommException in 90% of cases. The rest 10% can leave with Network being a sane name.


-- 
Dmitry Olshansky
April 02, 2013
On 2013-04-02 11:59:39 +0000, Dmitry Olshansky <dmitry.olsh@gmail.com> said:

> 02-Apr-2013 15:35, Andrei Alexandrescu пишет:
>> On 4/2/13 7:24 AM, Dmitry Olshansky wrote:
>>> You might want to add Visitor pattern to Exceptions but it's darn messy
>>> to deal with and is an overkill most of the time.
>> 
>> Actually I think that's a good thing to do.
>> 
> 
> Why would be that? It doesn't solve the key problem of "try clause plus a ton of semi-identical catches" used just to perform a mapping of X handlers to Y subsets of errors. Plus visitor does the same dispatch that is already addressed by exception handlers (or partly so).

What would be nice is some syntactic sugar for the following pattern:

	void handler(CommonExceptionType e)
	{
		// do something with exception
	}

	try { … }
	catch (FooException e) { handler(e); }
	catch (BarException e) { handler(e); }
	catch (BazException e) { handler(e); }

That could become:

	try { … }
	catch (CommonExceptionType e in FooException, BarException, BazException)
	{
		// do something with exception
	}

I've been secretly wishing for something like this, and not just in D.

-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca/

April 02, 2013
On Tue, Apr 02, 2013 at 09:43:20AM -0400, Andrei Alexandrescu wrote:
> On 4/2/13 7:59 AM, Dmitry Olshansky wrote:
> >02-Apr-2013 15:35, Andrei Alexandrescu пишет:
> >>On 4/2/13 7:24 AM, Dmitry Olshansky wrote:
> >>>You might want to add Visitor pattern to Exceptions but it's darn messy to deal with and is an overkill most of the time.
> >>
> >>Actually I think that's a good thing to do.
> >>
> >
> >Why would be that? It doesn't solve the key problem of "try clause plus a ton of semi-identical catches" used just to perform a mapping of X handlers to Y subsets of errors. Plus visitor does the same dispatch that is already addressed by exception handlers (or partly so).
> 
> Visitor allows centralized and flexible handling of exceptions.
> 
> >If somebody comes up with a reasonable Visitor pattern for Exceptions that is flexible and fast then sure let's see it. I just doubt it'll help anything on its own in any case.
> 
> Well I think exceptions + factory + visitor is quite the connection.
[...]

Hmm. So one could have something like this, perhaps?

	void main() {
		try {
			dotDotDotMagic();
		} catch(Exception e) {
			e.handle(ExceptionHandler());
		}
	}
	class ExceptionHandler {
		void handle(IOException e) { ... }
		void handle(ParseException e) { ... }
		...
	}

Of course, this would be the lowered version of some nice syntactic sugar that the compiler would translate into.

But I'm not sure how this offers an advantage over the current state of things, though. How would you handle the case where some exception types aren't handled by ExceptionHandler? How would you handle accepting multiple exception types by the same handler code? How would you map something like this to nicer syntax that isn't worse than the current way of just manually defining a common function (or overloaded functions) that handles the exception?

Or perhaps I didn't quite understand how you intend to implement the visitor pattern in exceptions.


T

-- 
Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce