Exceptions are much better for large systems. You can define standardised behaviour regardless of where an error occurs. You can let unexpected errors 'bubble up' and be managed in a way that preserves the integrity of the system. You have the flexibility to recover from errors and let errors be silenced.
There is unparalleled flexibility with exceptions. Go's approach is frankly arcane.
You are 100% right on unparalleled flexibility. Why limit your self to one set of flow control operators when you can have two! Also, lets make one break out of another one at any point of exception! The flexibility is endless!
You are 100% right, it is terrifyingly flexible. There is a reason exceptions as flow control is considered an anti-pattern.
So what you're saying is that the fundamental core of Java, Scala, Clojure are anti-patterns ? That's a strong statement for a platform that almost every enterprise application uses.
Go's error handling approach is also flow control. You receive an error and have to go and do something different based on that error. With Go it is explicit. With say Java you have the choice of being explicit or implicit. Flexibility is vital when you are working on large applications where the business logic varies wildly throughout the system.
Yes, we're saying the fundamental core of Java is an anti-pattern. Exceptions are dangerous. At any point in a function, you can suddenly jump out of that function due to an exception. It's certainly possible to write correct programs with exceptions, but it's quite hard, and almost no one does it correctly. If your code isn't chock full of try/finally blocks, then you're not doing it right.
Explicit is way better than implicit by an order of magnitude. It means you can look at any Go code and instantly understand what the control flow will be. You can't do that with languages that have exceptions.
Right - and we're saying that it's not. Exceptions handled incorrectly are not anymore dangerous than incorrectly handling an error in Golang. At least with exceptions, I can easily see the code that handles them in contrast to the code that doesn't.
>> At any point in a function, you can suddenly jump...
That's kind of the point. There's a separate path for exceptional behavior, which is what makes reasoning about exception paths easier.
>> It's certainly possible to write correct programs with exceptions, but it's quite hard, and almost no one does it correctly.
You keep saying that, but where's the proof that Golang programs are any more "correct"? How exactly are you quantifying that?
>> Explicit is way better than implicit by an order of magnitude.
That's like saying that having to think about putting one foot in front of the other is better than just walking down the street. It's rather ridiculous to say that.
>> It means you can look at any Go code and instantly understand what the control flow will be.
Not at all because your control of flow is now so concerned with error handling that you can't see the intended "happy path" logic.
The whole point is that exception handling code, when written robustly, looks almost like Go error handling code. That or you need to write a TON of magic cleanup classes which take a lot of time, are even more error prone, and less flexible than boring error handling code.
f, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open config file: %s", err)
}
// happy path
compare to:
File f;
try {
f = File.Open(filename)
}
catch (Exception e) {
throw new Exception(String.Format("failed to open config file: %s", e.Message))
}
// happy path
So, either you write code that looks like this, which is just like the Go code, or you don't, and you're handling your errors in a poor fashion.
What most people do, is they would let this function throw FileNotFoundException, and not catch it. There's at least three problems with that:
1. It leaks your implementation, so if later you're saving to memcache, you'll throw some different error, which could break consumers that have written code to handle the FileNotFoundException
2. It lacks context: file not found: foo.yaml ... wtf is foo.yaml and why was this random function trying to open it?
3. You can't tell programmatically what failed. Something threw a filenotfound... but you don't know if it was opening the config file or some other function, so you can't handle it properly.
There's also the fact that now anyone who calls your function needs to wrap it in a try/catch, or let an exception from 2 levels deep bubble up. So you can get FileNotFound: foo.yaml from your Initiate() function, and no one knows what that means.
That's what I mean by "almost no one [handles exceptions] correctly".
>So what you're saying is that the fundamental core of Java, Scala, Clojure are anti-patterns ? That's a strong statement for a platform that almost every enterprise application uses.
Just so. You think something being used in enterprise is a worthy argument?
It's sadly much too common to abuse exceptions for non-exceptional situations.
That doesn't mean Go's "error handling" is any better.
It's probably the worst approach one can pick. (But this has been discussed dozens of times already and Go fans have decided that they don't want to hear it.)
>It's probably the worst approach one can pick. (But this has been discussed dozens of times already and Go fans have decided that they don't want to hear it.)
Rust and Swift designers picked a similar approach. Is it possible that three separate designers of languages are so bound by language trends that they decided to make the almost exact same mistakes?
Saying exceptions are for exceptional situations is an old fallacy (one I used to believe in). Exceptions are errors. Errors happen all the time. They are not exceptional.
Sure they are. Just because something happens all the time doesn't mean that it's not exceptional. Banks get robbed all the time, but that's still the exception to the rule that people usually go to the bank for normal business.
At the "Bank of Golang" though - they've made every customer get strip searched on their way in because of something that happens 1% of the time. No thanks. I'd rather go across the street where they simply have some cameras and security guards watching the crowd for exceptional behavior.
I don't seem to understand what about returning error values stops any flexibility. I have built several things in Golang, and you absolutely have the ability to continue to return error values ("bubbling up") until a function has the ability to handle or silence it.
I get the personal preference one way or another, but can you go into more detail about what specific features one loses with Go's approach?
Indeed the majority of the time the correct way to handle an error is to propagate it up to the caller...no shit. which is why requiring manual plumbing everywhere to do just that is ridiculous. And forgetting this manual check even once is devastating.
And exceptions are not the only thing that do this automatically for you, so this is just flat out horrible design decision.
I deal with systems that contain millions* of lines of C++. Exceptions are killing us. How much larger do we need to get until I can expect to see the advantages?
* I haven't actually counted those lines or even wc -l'd them.
Exceptions are much better for large systems. You can define standardised behaviour regardless of where an error occurs. You can let unexpected errors 'bubble up' and be managed in a way that preserves the integrity of the system. You have the flexibility to recover from errors and let errors be silenced.
There is unparalleled flexibility with exceptions. Go's approach is frankly arcane.