Personally I dislike exceptions. It seems other people do as well:
http://stackoverflow.com/questions/1736146/why-is-exception-handling-bad
http://blogs.atlassian.com/2011/05/exceptions_are_bad/
etc... etc...
But the alternatives aren't any better, and in fact they only use a different syntax for the same thing, maybe with a slight 'benefit' that they don't have to have the same restrictions that the language designers intended. This doesn't seem better at all to me.
It seems like people can't decide how to handle it when things go wrong. There's your problem - things going wrong! Why is code designed in such a way that it can go wrong? It isn't.
The only time code tends to go wrong is with bad user-input. So why even accept bad user input in the first place? What I commonly see exceptions being used for is catching a user-input error 100 stack levels down from where the user input was originally accepted.
How does ParseNumberFromString handle something that isn't a valid number in string format? It doesn't, because that's not its responsibility - its responsibility is to do the best it can at parsing a number from that string. If it can't it can return a default value provided to it or just return 0. The calling code
should be is responsible for making sure everything is valid. Otherwise, you're breaking encapsulation.
By handling invalid user input as soon as possible, you decide exactly how your code reacts - if it fails, if it informs the user, if it continues silently in a corrupt state - you decide based on your needs and the application.
If you take invalid input, don't leave it to a function lower down or higher up in the call stack to deal with it - handle it, fix it, or ignore it right then and there. I've certainly never seen a person rely on the car or its manufacturer to handle the case when they start their car with no gas. It's invalid user-input, so the results are implementation-defined.
What happens when something is implementation defined? People don't do it :) So not handling an error state that is your responsibility should be considered as having the same taboo as being implementation defined.
So what if you ask a library to parse a JSON for you, and that JSON is invalid? You can't tell because only the library knows what is valid or not. Many would say that the client code should be held responsible, since it gave the library that bad user input, so therefore the library should call back and ask the calling code what to do. But doesn't this sound familiar? Stopping and going up the stack to see if the calling code can deal with it? It is just a different kind of exception handling.
But let's think here - if the JSON is invalid, we probably don't want to try using its data, so this case shouldn't even be handled. So you would receive false from parse() and handle there, right? This sounds familiar - stopping and going up the stack to let the calling code deal with it. Yes, we've just found yet another way to do exception handling.
So what can you do? Well, either you have to deal with the poorly designed library, or you can fork it since it's open source and fix the problem. Have it take a default state, e.g. a default json to parse that is guaranteed to be valid or else the result will be empty. Have a fallback - never deal with failure. If you need to know that it's the default state, include that as part of the default.
This is what you would do in real life.
You wouldn't let your car deal with you not putting gas in it.
You wouldn't let the dealership deal with you not putting gas in your car.
You wouldn't deal with your kid not putting gas in their car.
You wouldn't ask your parent to deal with you not putting gas in your car.
Either you put gas in your car, or you walk to work. You have something to fall back on - don't deal with it, just use an alternative guaranteed to work.
Some people complain about having to have default values everywhere, or about having to validate user input. I think this is ludicrous to complain about it - you only have to do it once in your code and never again, and it properly encapsulates everything.
Solution: Either guarantee you're operating with valid data, or provide default valid values to use, possibly including the fact that they are default values.
Please argue any points you find may be incorrect, you don't understand, or you don't agree with - I'm always open to new ideas and can admit that I am wrong. Also note that I actively came up with the answers and solutions to my questions and problems as I wrote this, so I may have contradicted some of my previous statements.