Submitted by Emmanuel Stapf on Thu, 12/08/2011 - 22:31.
Currently EiffelBase I/O handling will raise an exception if an operation fails. Meaning that one should not forget about catching exceptions during I/O operations otherwise your program might simply crash and exit.
The alternative we are considering is to change that behavior to set a status flag after each I/O operation that a user would have to check to ensure the operation proceeded successfully. In that case, your program will never crash but it might not behave correctly if one forget to check for that status flag.
Submitted by Peter Gummer on Fri, 12/09/2011 - 00:42.
"More details here", Manu? Where?
Anyway, here are my thoughts.
The normal Eiffel approach is to raise an exception only if there is a violation of a an assertion, i.e. if the program itself is incorrect.
I/O errors (lack of disk space, expected file does not exist, etc.) are environmental conditions. Running out of disk space isn't a bug in the program. Although it isn't an absolute principle in Eiffel that exceptions should not be raised due to environmental considerations, the current EiffelBase I/O handling runs contrary to how we habitually think about exceptions in Eiffel, so there's a big risk that we won't write the necessary rescue clause. When programming in C#, sure, I would always write a try ... catch block around I/O operations. This is habitual when programming in C# (or Java or C++, etc.). It's not a habit in Eiffel.
I don't like the idea of making this a configurable option. If one programmer writes a class using the status flag option, and then another programmer reuses that class in a different system that throws exceptions for file I/O, then the class will behave differently.
Although I would prefer a status flag, exceptions do have the advantage of being more likely to be noticed by the programmer during testing. But there's no guarantee of that either. Neither option seems perfect.
The best solution would be compile-time verification that the necessary checks were being done. Something like Java's checked exceptions might be good for this purpose (although we definitely wouldn't want all exceptions to be "checked"). Maybe some kind of design pattern involving void-safety checks could achieve this goal. For example, opening a file might return some object for manipulating the file or else Void on failure; an object test would be necessary in order to manipulate the file and compile successfully. It's not obvious to me, however, how we could extend such a pattern to commands such as writing to a file.
Submitted by Emmanuel Stapf on Fri, 12/09/2011 - 01:04.
I used `here' to mean my comment entry. The poll interface does not let you put a detailed explanation for the question. I used the comment entry to explain a little bit more what we had in mind when creating the poll.
Submitted by Berend de Boer on Fri, 12/09/2011 - 09:21.
Peter, an exception is a very helpful way of bailing. For many classes of software you cannot continue after disk full. You just gotta quit. As quitting is so often the right thing to do, having to write checks and then write the quit, is completely annoying.
But the best solution as already been found: programmers who write real world software write libraries that can do both. So depending on the use case you get an exception (the default), or you turn them off, and you deal with them manually.
Submitted by Peter Gummer on Sat, 12/10/2011 - 09:44.
Having a configuration option that allows you to choose whether or not an exception will be thrown sounds like the worst possible solution to me, Berend, because when writing some class I have no way of knowing how it's going to behave in any given system.
But maybe a configuration option is not what you meant. Maybe you're suggesting that the option should be controlled by a command on the relevant I/O classes ... something similar to how container classes allow clients to decide whether to do object comparisons or reference comparisons. That would be acceptable, in my view.
I do think that by default it should not throw exceptions, however, due to the principle of least surprise. Eiffel programmers don't normally expect exceptions to be raised, except for assertion violations. If some client class wants an exception to be thrown then I think the class should have to opt in.
Submitted by Emmanuel Stapf on Thu, 04/19/2012 - 05:18.
We had some internal discussion about this. And there are many solutions with pros and cons.
A global setting: A boolean flag that one can set on and off. Pros: Easy. Cons: A library written assuming no exception will be broken if global setting is set to exception.
A per object setting: A boolean attribute that one can set on and off. Pros: Pros: User can control if they want either or behavior. Cons: Before any I/O operations, you need to set the mode to what you expect.
A query based solution: By default I/O objects don't raise exceptions, if you want exceptions, you ask `with_exceptions: like Current' which returns you an object on the same I/O that will raise exceptions, conversely you have `without_assertions: like Current'. Pros: User can control what they want. Cons: It still doesn't prevent you from having to request the right object before each operations.
A feature based solution: Duplicate all routines and have one set raise an exception and the other one without exception. Pros: User is really in control. Cons: Implementors have to double the work, and it might confuse users in finding which feature they should call.
A type based solution #1: Duplicate all the types,once set will not raise an exception, and the other will. We might use a query to go back and forth between the two set of types. Pros: User knows what he is manipulating. Cons: Implementors have to double the work.
A type based solution #2: Preserve the existing set of classes but without exceptions, create a new generic class which is basically a wrapper around one of the I/O class that will raise an exception in case of an error. Pros: Simple and no surprises. Cons: It will only support the features of IO_MEDIUM and not of the descendant classes; so it might be required to have to write new descendants to cover certain cases and it becomes like type based solution #1.
I personally prefer the last proposal but looking at the complexity, I'm figuring that not having exceptions and not offering the opportunity to raise exception is what we should do to keep things simple.
Submitted by Emmanuel Stapf on Thu, 12/08/2011 - 23:41.
That's an option we had in mind, but I should have emphasized that we were talking about choosing the best default and want to have users provide their input on that.
Submitted by Berend de Boer on Fri, 12/09/2011 - 09:18.
best default: exceptions. But exceptions don't work in all cases. If you want reliable software, you need to be able to turn that off, because as soon as you have an exception, your class invariants get to an unknown state.
Comments
More details below
Currently EiffelBase I/O handling will raise an exception if an operation fails. Meaning that one should not forget about catching exceptions during I/O operations otherwise your program might simply crash and exit.
The alternative we are considering is to change that behavior to set a status flag after each I/O operation that a user would have to check to ensure the operation proceeded successfully. In that case, your program will never crash but it might not behave correctly if one forget to check for that status flag.
Which alternative do you prefer?
Exceptions caused by environmental factors
"More details here", Manu? Where?
Anyway, here are my thoughts.
The normal Eiffel approach is to raise an exception only if there is a violation of a an assertion, i.e. if the program itself is incorrect.
I/O errors (lack of disk space, expected file does not exist, etc.) are environmental conditions. Running out of disk space isn't a bug in the program. Although it isn't an absolute principle in Eiffel that exceptions should not be raised due to environmental considerations, the current EiffelBase I/O handling runs contrary to how we habitually think about exceptions in Eiffel, so there's a big risk that we won't write the necessary rescue clause. When programming in C#, sure, I would always write a try ... catch block around I/O operations. This is habitual when programming in C# (or Java or C++, etc.). It's not a habit in Eiffel.
I don't like the idea of making this a configurable option. If one programmer writes a class using the status flag option, and then another programmer reuses that class in a different system that throws exceptions for file I/O, then the class will behave differently.
Although I would prefer a status flag, exceptions do have the advantage of being more likely to be noticed by the programmer during testing. But there's no guarantee of that either. Neither option seems perfect.
The best solution would be compile-time verification that the necessary checks were being done. Something like Java's checked exceptions might be good for this purpose (although we definitely wouldn't want all exceptions to be "checked"). Maybe some kind of design pattern involving void-safety checks could achieve this goal. For example, opening a file might return some object for manipulating the file or else Void on failure; an object test would be necessary in order to manipulate the file and compile successfully. It's not obvious to me, however, how we could extend such a pattern to commands such as writing to a file.
I used `here' to mean my
I used `here' to mean my comment entry. The poll interface does not let you put a detailed explanation for the question. I used the comment entry to explain a little bit more what we had in mind when creating the poll.
Peter, an exception is a very
Peter, an exception is a very helpful way of bailing. For many classes of software you cannot continue after disk full. You just gotta quit. As quitting is so often the right thing to do, having to write checks and then write the quit, is completely annoying.
But the best solution as already been found: programmers who write real world software write libraries that can do both. So depending on the use case you get an exception (the default), or you turn them off, and you deal with them manually.
Libraries that can do both
Having a configuration option that allows you to choose whether or not an exception will be thrown sounds like the worst possible solution to me, Berend, because when writing some class I have no way of knowing how it's going to behave in any given system.
But maybe a configuration option is not what you meant. Maybe you're suggesting that the option should be controlled by a command on the relevant I/O classes ... something similar to how container classes allow clients to decide whether to do object comparisons or reference comparisons. That would be acceptable, in my view.
I do think that by default it should not throw exceptions, however, due to the principle of least surprise. Eiffel programmers don't normally expect exceptions to be raised, except for assertion violations. If some client class wants an exception to be thrown then I think the class should have to opt in.
Exception or not Exception? That is the question!
We had some internal discussion about this. And there are many solutions with pros and cons.
I personally prefer the last proposal but looking at the complexity, I'm figuring that not having exceptions and not offering the opportunity to raise exception is what we should do to keep things simple.
Like eposix does: have both.
Like eposix does: have both. Default is exception, you can turn that off.
That's an option we had in
That's an option we had in mind, but I should have emphasized that we were talking about choosing the best default and want to have users provide their input on that.
best default: exceptions. But
best default: exceptions. But exceptions don't work in all cases. If you want reliable software, you need to be able to turn that off, because as soon as you have an exception, your class invariants get to an unknown state.