Posted Mar 31, 2012 0:44 UTC (Sat) by cmccabe (guest, #60281)
[Link]
"File not found" isn't a programmer error, so it shouldn't cause an exception.
Uses
Posted Mar 31, 2012 0:52 UTC (Sat) by Cyberax (✭ supporter ✭, #52523)
[Link]
Ok. You have:
>bool is_propery_set(context, prop_name);
This can cause delayed configuration loading and reading with a potential for FileNotFound error. What are you going to do in this case?
I advise:
> enum Bool { True, False, FileNotFound };
Uses
Posted Mar 31, 2012 1:29 UTC (Sat) by cmccabe (guest, #60281)
[Link]
I wouldn't advise calling anything Bool that has more than 2 possible values. Or trying to reimplement primitive types.
A common Go idiom is returning a tuple that includes an error code.
Uses
Posted Mar 31, 2012 5:44 UTC (Sat) by Cyberax (✭ supporter ✭, #52523)
[Link]
So your code instead of:
if (is_set(ctx, 'debug_mode') && is_set(ctx,'show_log'))
// do_something();
Becomes:
(debug_mode, err1) = is_set(ctx, 'debug_mode')
if (err1) return err1;
(show_log, err2) = is_set(ctx, 'show_log')
if (err1) return err1;
if (debug_mode && show_log)
// do_something()
So a one-liner quickly turns into an unreadable ream of error-prone source code.
But let's go further. Suppose I have a lazy-loaded collection that exposes a Map interface. I.e.
map[string,string] m = make_lazy_map(...);
string a = m.get('hello')
The code that works with this map might not even know that it can return errors. What are you going to do in this case?
Exceptions are complex because they solve a complex task. And any attempt to solve it any other way leads to Yet Another Dumb Exception Framework, as Go show us.
Uses
Posted Mar 31, 2012 7:56 UTC (Sat) by fdr (subscriber, #57064)
[Link]
If one needs to short circuit for a major fault, use panic/recover. Accrue low-level cleanup actions using defer expressions. Done. I had the same problem you describe and asked some Go-people about it, and then pointed me to a module that used that pattern. It works fine, and doesn't even require a large-scale understanding of the code using said pattern.
panic and recover are there to help with major faults and stack unwinding, and return codes for detailed error recovery that, frankly, are mechanically ugly to complete without being in the low-level mechanism near the failure.
The convention is "must" will panic if something goes wrong. must really is lowercase here, because one is not to panic across library boundaries.
Uses
Posted Mar 31, 2012 17:00 UTC (Sat) by Cyberax (✭ supporter ✭, #52523)
[Link]
Duh. panic/recover is a lame implementation of exceptions. So one certainly can use them for these scenarios.
The problem is, they've squandered opportunity to do something genuinely better, like enforcing the exception safety.
Uses
Posted Mar 31, 2012 22:52 UTC (Sat) by cmccabe (guest, #60281)
[Link]
I am going to use some Java terminology here, so bear with me. "Unchecked" exceptions are those that are not required to be caught by anyone. In other words, they are not enforced by the type system. "Checked" exceptions, on the other hand, must be either declared to be thrown by the caller, or handled by the caller.
Unchecked exceptions remove information from the type system, which makes the system less robust, for the same reason using void* and not enforcing invariants does. C++, Python, and many other languages ONLY have unchecked exceptions. In many cases, it's difficult even to get information about what exceptions can be thrown, let alone what to do about them, without reading the entire source code of the function you're calling and all the functions it calls. I've been in this situation before, and it's not pretty. For me at least, this kind of "error handling" is really "error ignoring" since the likelihood that maintenance programmers will know all that stuff approaches zero as time goes on.
Checked exceptions, on the other hand, tend to be just as verbose (and sometimes more verbose) than simple if-then error handling. For example:
I'm giving you a best-case scenario here-- I didn't have to add a finally block or do something special with RuntimeException, which I often do.
And if you think it's always possible to change your function signature to declare all the checked exceptions you want to throw, think again. For example, if you are implementing an interface like SAXParser or something, you don't get that kind of choice. You'll have to catch and translate every checked exception into either a RuntimeException (which ruins type-safety) or a SAXParseException (which is misleading and verbose to implement).
A final comment. If you design the system such that there are a lot of corner cases are tricky situations, then yes, your code will be tricky. On the other hand, if you design the system such that it's simple, you won't feel the need for complexity. For example, you could load the entire configuration at once and keep it in memory, eliminating the need to handle the "file not found" error condition that you're so worried about. Exceptions don't reduce the complexity of code. At best, they just sweep it under the rug. And that's not a good thing, for anyone concerned.
Another note. In its newest language, Ceylon, Red Hat is getting rid of checked exceptions. Google coding standards for C++ dictate no use of exceptions-- ever. I think the way Go handles exceptions is the right way.
Uses
Posted Mar 31, 2012 23:33 UTC (Sat) by Cyberax (✭ supporter ✭, #52523)
[Link]
Well, checked exceptions are a failed idea for many reasons.
First, having an exact exception type in a function signature is BAD. That means that you are expecting these exceptions to be thrown sometime and this means you are most probably better off using return codes in this case.
C++ has a better idea - exception guarantees ( http://en.wikipedia.org/wiki/Exception_guarantees ). I rarely care about types of exceptions but I really care about the fact that exceptions can occur at all. Having a type system enforcing correct exception safety would be really nice.
C++ moves in this direction, but slowly.
>A final comment. If you design the system such that there are a lot of corner cases are tricky situations, then yes, your code will be tricky. On the other hand, if you design the system such that it's simple, you won't feel the need for complexity. For example, you could load the entire configuration at once and keep it in memory, eliminating the need to handle the "file not found" error condition that you're so worried about.
How about lazy-loading of entities from database or memcached? Or maybe a failure to perform action because there's not enough space for audit log entry? There are lots of cases where exceptions can happen.
Callbacks are another case - what happens if a callback encounters an error?
>Exceptions don't reduce the complexity of code. At best, they just sweep it under the rug. And that's not a good thing, for anyone concerned.
Nope. Exceptions help to deal with a thorny problem - non local effects of errors.
>Google coding standards for C++ dictate no use of exceptions-- ever. I think the way Go handles exceptions is the right way.
Google doesn't use exceptions because of legacy. They have a lot of exception-unsafe code and it's just too complex to convert it.
Uses
Posted Apr 1, 2012 2:37 UTC (Sun) by cmccabe (guest, #60281)
[Link]
> C++ has a better idea - exception guarantees
You really need to get out of the mentality that C++ is a single thing that "has ideas." Even aside from the obvious anthropomorphism, there is no single person or company that controls the direction of C++. New features are added by a committee. Over the years, an amazing number of features got added. Some of them are useful, some of them are not. Some of them are useful, but interact poorly with other useful features. In the real world, companies choose the subset of features they like the best, and move on.
I programmed C++ for almost 10 years. I've seen a lot of different styles of code in that time. I was once a passionate advocate of exceptions, Boost, and design patterns. My opinions changed a lot when I saw the real-world messes these things made. Reading some of the Golang documents, I found myself agreeing with every point they made. Finally, someone with real-world experience building systems and good taste was designing a successor to C++.
> http://en.wikipedia.org/wiki/Exception_guarantees ). I rarely care about
> types of exceptions but I really care about the fact that exceptions can
> occur at all. Having a type system enforcing correct exception safety
> would be really nice.
The only "guarantees" worth the name are those that are enforced by the compiler. Calling something a "guarantee," when it may or may not be true, is an Orwellian misuse of the language-- like calling a DRM format "PlaysForSure." In my experience, only the best programmers can have even a chance of writing exception-safe code in C++. And even they make mistakes, being human.
> [snip examples]
If it's a programmer error, then panic. If it's anything else, return an appropriate result, whatever that may be.
> Exceptions help to deal with a thorny problem - non local effects
> of errors.
To do proper error handling, you need to clean up local resources. Do exceptions help with that? Nope, it's your job-- even in Java or Python. You need to decide how to handle the problem. Do exceptions help there? Nope. At the end of the day, they are no more than a "goto," whitewashed with a shiny new coat of object-oriented paint.
Uses
Posted Apr 1, 2012 6:41 UTC (Sun) by Cyberax (✭ supporter ✭, #52523)
[Link]
Well, I know that C++ doesn't like it when people anthropomorphize it. But it's easier than saying 'some members of C++ committee'.
Golang already shows its ugly areas - people shouldn't allow C developers to design languages. And as I've said, error handling is one of the ugly parts.
I've written a fair amount of code in Go and I've already encountered cases where changes to functions required addition of 'err' parameter, breaking my code in the process.
A some libraries now have all the functions returning Error as the second parameter in all functions, even if it's never actually used but might be used some day in future. Which is crazy.
>The only "guarantees" worth the name are those that are enforced by the compiler.
Go instead decided to make a step back - it HAS exceptions (a rose by any other name...) but they are fugly.
>If it's a programmer error, then panic. If it's anything else, return an appropriate result, whatever that may be.
That's the result of map.get('something') if map itself misbehaves?
>To do proper error handling, you need to clean up local resources. Do exceptions help with that?
YES, THEY DO!!!
In C++ exception force you to use RAII to manage resources. That ensures that resources are automatically released during the normal execution flow and simply CAN'T be leaked.
While explicit error code handling practically ensures that some cleanup actions are going to be missed. That's why the most common error handling pattern in plain C is 'goto cleanup' which essentially emulates the stack unwinding.
Uses
Posted Apr 1, 2012 23:18 UTC (Sun) by cmccabe (guest, #60281)
[Link]
> I've written a fair amount of code in Go and I've already
> encountered cases where changes to functions required
> addition of 'err' parameter, breaking my code in the process.
One of the worst APIs I've used was the original wireless.h (aka "wireless extensions") API in the Linux kernel. Basically, it forwards you a bunch of ioctls that come from userspace. Except it doesn't bother to parse them at all-- it's YOUR job to parse a giant blob of bytes, try to pick out fields, and do pointer-chasing.
When asked why this was so, the author simply replied, "if I had provided a callback API, I would have had to update all the callback implementors each time I added a new field." (I'm paraphrasing a bit; I can't find the original email.) When I read this response, I couldn't stop laughing for about a minute. Well of COURSE you have to change the callback implementors when the ioctl changes. That's the point of the type system! But he couldn't see it. Later wireless.h was replaced by the excellent nl80211 API.
I think, like him, you may be missing the point here. The point of the type system is to make guarantees. Real guarantees, not just conventions. "I will not encounter any errors" is just such a guarantee, and that's what you're promising when you don't provide an error return code.
[snip discussion of noexcept]
C++ already HAS a way of declaring that a function won't throw-- add "throw()" to the end of the function. They're going to add another way?
And apparently this new way will also just be a programmer promise with no real weight:
> This technically allowed the possibility that a noexcept function could
> still throw: this would trigger an undefined behavior. In addition,
> current exception specifications in C++03 were to be deprecated.
>
> Then, Rani Sharoni proposed a different solution... [snip]
Yeah. Sounds like progress. C++ throw specifications are FUBAR, and a change like this will just turn the pain level up to 11. You'll be dealing with two __different__ broken exception specification systems. Good luck dealing with library code that may not get updated to the new system for years!
Even if they managed to sort out this mess, they'd still just be re-inventing Java checked exceptions, which most people find verbose and annoying.
> In C++ exception force you to use RAII to manage resources. That ensures
> that resources are automatically released during the normal execution flow
> and simply CAN'T be leaked.
>
> While explicit error code handling practically ensures that some cleanup
> actions are going to be missed. That's why the most common error handling
> pattern in plain C is 'goto cleanup' which essentially emulates the stack
> unwinding.
Exceptions and RAII are two different things. For example, Google does not use exceptions, but they make heavy use of RAII. Golang also has its own version of RAII, the defer statement. And, of course, garbage collection means that there is a lot less cleanup to do.
Anyway, even Herb Sutter admits that the best exception-safe code which implements a certain algorithm may sometimes be less efficient than the best non-exception-safe code. And he's an enthusiastic advocate of exceptions. So my question to you is, if you're going to sacrifice efficiency in this way, why use a low-level, non-garbage collected, memory-unsafe language in the first place?
Uses
Posted Apr 2, 2012 14:34 UTC (Mon) by jwakely (subscriber, #60262)
[Link]
C++ already HAS a way of declaring that a function won't throw-- add "throw()" to the end of the function. They're going to add another way?
A non-empty exception spec i.e. throw(X,Y,Z) is deprecated, but you're left with (as before) throw() meaning the function won't throw or if that's not given then (as before) it means the function might throw, and could throw any type.
But in order to make throw() useful for metaprogramming there's a new way to spell it which is noexcept(true) or simply noexcept. You can also say noexcept(false) to say a function might throw, which seems useless until you realise the boolean can be any constant expression, so a function template with parameter T can be declared noexcept(is_nothrow_copyable<T>::value) i.e. the function guarantees not to throw if copying T won't throw. There is also a noexcept operator that can be used to query whether arbitrary expressions will throw.
It's not a new mechanism, it's the same one with the useless part (non-empty exception specs) removed, and more useful syntax.
If library code isn't updated to the new mechanism it doesn't matter, it still works fine (but noone should be using non-empty exception specs anyway, they're crap) and throw() is equivalent to noexcept.
Uses
Posted Apr 2, 2012 15:00 UTC (Mon) by jwakely (subscriber, #60262)
[Link]
(It's not _precisely_ the same, but the differences for most people's uses are equivalent or are an improvement anyway.)