February 18, 2012
On Sat, Feb 18, 2012 at 08:20:23PM +0100, deadalnix wrote: [...]
> However, in D, I think this is more the role of an Errors. Exception are something « softer ». It will alert you on problems your program encounter, but that are recoverable.

I agree.


[...]
> Let's get an exemple : your program ask a file to the user and do some operations with this file. If the file doesn't exists, you can prompt for another file to the user with a meaningful message and start again. However, the first version of your program can just ignore that case and fail with a less specific handler in firsts versions.
> 
> You cannot achieve something like that if you don't have a useful type to rely on. Here something like FileNotFoundException is what you want.

Exactly.  It's important to distinguish between exceptions sometimes. For example:

	File promptSaveFile() {
		do {
			string filename = readInput();

			try {
				return File(filename);
			} catch(FileNotFoundException e) {
				writeln("No such file, please try "
					"again");
			}
		} while(true);
		assert(false);
	}

It would not make sense for that catch to be *any* Exception; for example, if the exception was ReadFailureException, you do *not* want to catch that, but you want to propagate it.

But sometimes, you *want* to handle ReadFailureException, for example:

	void salvageBadSector(out ubyte[] sector) {
		int n_attempts = 10;

		while(n_attempts > 0) {
			try {
				disk.read(sector);
			} catch(ReadFailureException e) {
				disk.recalibrate();
				n_attempts--;
				continue;	// try again
			}
			// If another error occurs, say OutOfMemory,
			// or some other unexpected problems, you do NOT
			// want to continue.
		}

		writeln("Cannot salvage data from bad sector");
	}

Having distinct exception types allows you to do this without resorting to hacks, or bypassing the library altogether (because of the use of overly generic exception types).

Having a proper exception class hierarchy is also necessary. For example, most programs don't care about the difference between FileNotFoundException and ReadFailureException; they just want to print an error message and cleanup if I/O fails:

	Data loadData() {
		try {
			return readFromFile();
		} catch(IOException e) {
			writeln("I/O error occurred, aborting");
			cleanup();
			exit(1);
		}

		assert(false);
	}

It would be wrong to just catch Exception here, because if it wasn't an I/O error, but something else, it needs to be propagated!


> The type of the exception must depend on the problem you are facing, not on the module that trhow it. I see a lot of people doing the « MyProgramException » or « MyLibException » but that doesn't make sense. In this case, you are just making things harder.

Agreed.


[...]
> If this politic is choosen, then It would make sense to have several modules of phobos throwing exceptions of the same type, or inheriting from the same base class.

Phobos NEEDS to have a sane exception hierarchy. ESPECIALLY since it is the standard library. So you cannot assume your users cares or don't care about distinguishing between exception types. Some applications just catch Exception, cleanup and exit, but other applications need to know the difference between FileNotFoundException and ReadErrorException.

So we need to design a good exception hierarchy for Phobos.

The worst thing is if Phobos exceptions are too generic, then applications that need to tell between different problems will have to bypass Phobos and hack their own solution. Which is not good. The standard library should be maximally useful to the extent that it can be.


> Exception type is a convenient way to filter what you catch and what you don't know how to handle at this point.

Agreed.


T

-- 
The right half of the brain controls the left half of the body. This means that only left-handed people are in their right mind. -- Manoj Srivastava
February 18, 2012
On 2/18/12, Nathan M. Swan <nathanmswan@gmail.com> wrote:
> It would be nice if there was a mixin template that creates an exception class that acts like this; making similar exception classes is annoying.

It would be even nicer if we didn't need a mixin for classes that simply forward the ctor call to the base class ctor:

class FileException : Exception { }   // no-go, have to write a ctor that forwards to super

AIUI this has something to do with ctors not being virtual. I think someone mentioned this could be improved one day.
February 18, 2012
On 2/18/12 1:27 PM, Alex Rønne Petersen wrote:
> Point taken. I suggested GetOptException initially because, based on
> usage patterns of std.getopt, it seems to be most common to just catch
> the exception (be it a parsing error, type coercion error, or whatever)
> and print it.

In fact for the vast majority of exceptions it seems to be most common to just catch the exception and print it.

Tutorial examples never help, either. They go something like:

try {
  ... stuff ...
} catch (DatabaseException e) {
  print("There was a database exception: ", e);
} catch (NetworkException e) {
  print("There was a network exception: ", e);
}

etc.

Andrei
February 18, 2012
On 18-02-2012 21:36, H. S. Teoh wrote:
> On Sat, Feb 18, 2012 at 08:20:23PM +0100, deadalnix wrote:
> [...]
>> However, in D, I think this is more the role of an Errors. Exception
>> are something « softer ». It will alert you on problems your program
>> encounter, but that are recoverable.
>
> I agree.
>
>
> [...]
>> Let's get an exemple : your program ask a file to the user and do some
>> operations with this file. If the file doesn't exists, you can prompt
>> for another file to the user with a meaningful message and start
>> again. However, the first version of your program can just ignore that
>> case and fail with a less specific handler in firsts versions.
>>
>> You cannot achieve something like that if you don't have a useful
>> type to rely on. Here something like FileNotFoundException is what
>> you want.
>
> Exactly.  It's important to distinguish between exceptions sometimes.
> For example:
>
> 	File promptSaveFile() {
> 		do {
> 			string filename = readInput();
>
> 			try {
> 				return File(filename);
> 			} catch(FileNotFoundException e) {
> 				writeln("No such file, please try "
> 					"again");
> 			}
> 		} while(true);
> 		assert(false);
> 	}
>
> It would not make sense for that catch to be *any* Exception; for
> example, if the exception was ReadFailureException, you do *not* want to
> catch that, but you want to propagate it.
>
> But sometimes, you *want* to handle ReadFailureException, for example:
>
> 	void salvageBadSector(out ubyte[] sector) {
> 		int n_attempts = 10;
>
> 		while(n_attempts>  0) {
> 			try {
> 				disk.read(sector);
> 			} catch(ReadFailureException e) {
> 				disk.recalibrate();
> 				n_attempts--;
> 				continue;	// try again
> 			}
> 			// If another error occurs, say OutOfMemory,
> 			// or some other unexpected problems, you do NOT
> 			// want to continue.
> 		}
>
> 		writeln("Cannot salvage data from bad sector");
> 	}
>
> Having distinct exception types allows you to do this without resorting
> to hacks, or bypassing the library altogether (because of the use of
> overly generic exception types).
>
> Having a proper exception class hierarchy is also necessary. For
> example, most programs don't care about the difference between
> FileNotFoundException and ReadFailureException; they just want to print
> an error message and cleanup if I/O fails:
>
> 	Data loadData() {
> 		try {
> 			return readFromFile();
> 		} catch(IOException e) {
> 			writeln("I/O error occurred, aborting");
> 			cleanup();
> 			exit(1);
> 		}
>
> 		assert(false);
> 	}
>
> It would be wrong to just catch Exception here, because if it wasn't an
> I/O error, but something else, it needs to be propagated!
>
>
>> The type of the exception must depend on the problem you are facing,
>> not on the module that trhow it. I see a lot of people doing the «
>> MyProgramException » or « MyLibException » but that doesn't make
>> sense. In this case, you are just making things harder.
>
> Agreed.
>
>
> [...]
>> If this politic is choosen, then It would make sense to have several
>> modules of phobos throwing exceptions of the same type, or inheriting
>> from the same base class.
>
> Phobos NEEDS to have a sane exception hierarchy. ESPECIALLY since it is
> the standard library. So you cannot assume your users cares or don't
> care about distinguishing between exception types. Some applications
> just catch Exception, cleanup and exit, but other applications need to
> know the difference between FileNotFoundException and
> ReadErrorException.
>
> So we need to design a good exception hierarchy for Phobos.
>
> The worst thing is if Phobos exceptions are too generic, then
> applications that need to tell between different problems will have to
> bypass Phobos and hack their own solution. Which is not good. The
> standard library should be maximally useful to the extent that it can
> be.

I'd just like to add to this that we don't lose anything per se by introducing specific exception types. People can still catch Exception if they really, really want.

>
>
>> Exception type is a convenient way to filter what you catch and what
>> you don't know how to handle at this point.
>
> Agreed.
>
>
> T
>

-- 
- Alex
February 18, 2012
On 02/18/2012 10:25 PM, Alex Rønne Petersen wrote:
>
> I'd just like to add to this that we don't lose anything per se by
> introducing specific exception types. People can still catch Exception
> if they really, really want.
>

We gain executable bloat. I don't know how how significant the amount would be.
February 18, 2012
On 2/18/12 3:25 PM, Alex Rønne Petersen wrote:
> I'd just like to add to this that we don't lose anything per se by
> introducing specific exception types.

1. Technical debt

2. Set an example that will be followed

3. Boilerplate


Andrei
February 18, 2012
On 18-02-2012 22:30, Timon Gehr wrote:
> On 02/18/2012 10:25 PM, Alex Rønne Petersen wrote:
>>
>> I'd just like to add to this that we don't lose anything per se by
>> introducing specific exception types. People can still catch Exception
>> if they really, really want.
>>
>
> We gain executable bloat. I don't know how how significant the amount
> would be.

For a single class that has nothing but a constructor? Negligible IMHO.

-- 
- Alex
February 18, 2012
>> You don't want to use specific exceptions because it couples unrelated
>> code.  The distinction of recoverable Exceptions and non-recoverable
>> Errors is good enough in most cases.
>
> Not true, especially in library code. If you try to open a file,
> FileNotFoundException should be recoverable, but DiskFailureException
> should not. You need to distinguish between them.
>
That's what I said. Exception and Error.

>> Typed exception being used for local error recovery is about the same
>> as using error codes but at a bigger expense. On the plus side
>> exception can carry more specific error messages, which could be
>> solved for error codes too.
>
> The assumption is that exceptional conditions are, well, exceptional, so
> it shouldn't matter if you require extra CPU/memory to handle them.
>
> The worst thing is if we enforce generic exceptions instead of specific
> exceptions, and then code that *needs* to distinguish between different
> types of errors must pattern-match the error message to tell which one
> it is. That is unmaintainable, full of opportunities for bugs and i18n
> problems, and is absolutely NOT the way we want to go.
>
If you can recover from errors you know beforehand what kind of errors
may occur. That remove may fail if you haven't checked the path is clear.
What I'm saying is that error returns are more flexible for interfaces that
have know error cases. You can then either directly handle them or you may
translate them to exceptions using enforce. Always using exceptions forces
a certain programming model on you.

uint toUint(int value)
{
    if (value < 0) throw new ConvUnderflowException("Negative Value");
    return cast(uint)value;
}

void main(string[] args)
{
    int ival = parse!int(args[1]);
    uint uval;
    try
    {
        uval = toUint(ival);
    }
    catch (ConvUnderflowException)
    {
        uval = 0;
    }
}
February 18, 2012
On Sat, Feb 18, 2012 at 10:38:06PM +0100, Alex Rønne Petersen wrote:
> On 18-02-2012 22:30, Timon Gehr wrote:
> >On 02/18/2012 10:25 PM, Alex Rønne Petersen wrote:
> >>
> >>I'd just like to add to this that we don't lose anything per se by introducing specific exception types. People can still catch Exception if they really, really want.
> >>
> >
> >We gain executable bloat. I don't know how how significant the amount would be.
> 
> For a single class that has nothing but a constructor? Negligible IMHO.
[...]

Especially a ctor that does nothing but forwards arguments to the base class. It will just be inlined 100% of the time.


T

-- 
Любишь кататься - люби и саночки возить.
February 18, 2012
On Sat, Feb 18, 2012 at 03:36:40PM -0600, Andrei Alexandrescu wrote:
> On 2/18/12 3:25 PM, Alex Rønne Petersen wrote:
> >I'd just like to add to this that we don't lose anything per se by introducing specific exception types.
> 
> 1. Technical debt

I don't understand. Could you elaborate?


> 2. Set an example that will be followed

If it's a *good* example, it should be followed, no? ;-)


> 3. Boilerplate
[...]

Can be alleviated if there's a way to use templates to make exception subclasses.

But all of this is just the mechanics of the system. We should rather be debating about the conceptual/theoretic merits at this point, than get hung up about the nitty gritty details. That can come later once we've settled on the right conceptual model.


T

-- 
Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen