The error model of Microsoft's new language
plus.google.com
plus.google.com
Consider a web server. If we tried to make the entire web server one component, then hitting an out-of-bounds error when handling a request would take down the entire web server. That is bad. But if the main event loop of the web server is a component that depends on a separate component that handles requests, then the main event loop could recover from the out-of-bounds error in the page request component. This is what we should always do, of course, but it's being forced (or, heavily encouraged) by the programming model itself.
I'd like to see how "components" are defined to understand this better - or any code, really.
[0] http://channel9.msdn.com/Shows/Going+Deep/Joe-Duffy-Perspect...
Generally, I think a "component" would be a piece such that if there's an unplanned-for, fatal failure in it, other components need to continue running.
It just sounds like a mess. Not the worst mess in the world, to be sure, but error handling just isn't the way that I want to be forced to architect my system. Imagine, for example, I declared that primitive types are important, and thus you have to create components that either work with int, or double (or char vs unicode, or whatever). For some project you might just split things up like that anyway, but it would be pretty unusual in most cases. Or, if that seems contrived, I say const correctness is important, and thus components must be const or not. Elaborate with any language feature you consider important...
tl;dr: Yes, think long and hard about error handling. But no, I don't necessarily want/need/like organizing code into components based on that need. Error handling is only one of many competing pressures on how I might organize code.
Take the null reference example: A NullReferenceException means that a value's null in a spot where the programmer either didn't believe or didn't consider that null would be a possible value at that point in the program. So when it happens, there are two things you can be sure of: First, things have strayed off into the weeds. Second, there's nobody around to check everything out and make sure we can get steered safely back on track, because the developer's been out of the picture since the start of compile time. These two facts imply that letting execution continue would be A Roll of the Dice, and therefore Not a Good Idea.
If having the module crash in this situation isn't acceptable, no worries. There are still ways around it short of splitting the component in two. One much easier option would be:
if (x == null) { /* handle null case */ }One is that it creates some predictability around how errors will be handled. As a producer of components, I know that within this component I have to accept that certain classes of error will always cause a fast failure, and other classes of error should be handled as immediately and gracefully as possible. Compared to the mishmash of different error handling philosophies that I currently encounter when working on different components, this seems like it might be a welcome improvement even if it isn't a complete solution.
As a consumer of components, I think it's even better because it enforces still stronger predictability. I get a couple nice guarantees out of this. One is that critical failures bringing the whole component down means I encounter fewer situations where I'm unsure whether the overall application is in a consistent state, because 3rd-party component developers have fewer opportunities to do weird things with exceptions. The other is that I can always defend against critical failures in 3rd-party components bringing down the whole application. That isn't always easy to do in vanilla C#, where FailFast exceptions always bring down the house and the best defense against that is to resort to the hassle of process-level isolation. For systems programming I imagine such measures are even more irritating, since process-level isolation creates performance overhead.
That is not necessarily bad. You only want to keep orthogonal concepts independent if mixing them doesn't buy you something better (in this case that would be a design that defaults toward fault tolerance).
They both make the distinction between recoverable and unrecoverable errors. I feel D to be more flexible however since it doesn't prevent the handling of unrecoverable errors. It simply discourage it, which is what you want in a systems programming language.
One exceptional feature of D related to error handling are scope statements. They easily and elegantly make the nested try/catch/finally blocks go away. I wonder if Microsoft is going to use this.
D's scope guard is similar to "defer", but it has three forms: "scope(exit)", "scope(failure)", and "scope(success)". So instead of a deferred function that tests "if r := recover(); r != nil {...}" you just do "scope(failure) {...}"
D's unrecoverable error is just a matter of having an exception hierarchy where "Exception" and "Error" are separate subclasses of "Throwable", and by convention you prefer not to write "catch (Throwable t)" or "catch (Error e)".
Did a quick lookup on Go's defer and panic, it does indeed have similarities with D's scope. Go's defer looks like the equivalent of scope(exit) in D.
The big difference is that error handling in Go uses error codes while D uses a Throwable base class subclassed into Exception and Error for recoverable and unrecoverable errors respectively.
D also has scope(success) and scope(failure) to execute code blocks depending on whether a Throwable is raised or not.
I feel that 70% of what I write in unit tests and ifs-at-the-start-of-a-method should be in contracts, and the only reason they're expressed so cumbersome is because the languages I use don't support any better.
Even for a simple if: DRY! You should have put those in reusable functions with nice understandable names ages ago :]
Typical contracts are 'assert (arg != null)' or 'assert(arg1.length == arg2.length)' or 'assert (hour >= 0 and hour <= 23)'
if( x < 0 || x > 5 )
throw new ArgumentOutOfRange();
if( y == nullptr )
thwrow new NullRefException();
which should imo be something like Contract.AssertInRange( x, 0, 5 );
Contract.AssertNotNull( y ); public void MyFunction([InRange(0, 5)] int x, [NotNull] object y)
{
// ...
}
I just checked, and in fact Fody can do something just like this [1].The only downside I see is contracts like this would be hard to write concisely:
if (useFirstObject == true)
Contract.AssertNotNull(firstObject);
else
Contract.AssertNotNull(secondObject);
[1] https://github.com/Fody/NullGuard We definitely tried to learn from Eiffel. Our contract
system really isn't revolutionary at all; we just tried
to take the simplest and best parts of Eiffel, and
distill them down into a format that would feel familiar
and comfortable for C# developers.assert(p != NULL); // unrecoverable error
if (p == NULL) {
// no buffer for you but we'll continue
perror("malloc");
return (0);
}Without code, though, it's hard to see how this will actually work.
That's hand waving past any shared state but if you need that I suppose you use threads (I'm not a thread fan but I have to believe there is a thread_assert() that kills just that thread).
I'd think that should be a reason to encourage people who are trying new things in an effort to come up with one that works well, not a reason to offhandedly poo-poo anyone who isn't satisfied with the acknowledgedly crummy status quo.
More details will hopefully be coming soon, but until then we might as well stay calm and try not to confuse cocktail napkins with something that's ready for peer review.