We detached this subthread from https://news.ycombinator.com/item?id=38098729.
Btw, the site guidelines also include "Don't be snarky." - I realize that takes some of the fun out of posting a particular kind of comment, but people tend to overrate the quality of what they post when they post that way, and tend to underrate the degrading effect it has on discussions, and that's a double whammy of badness. Having a don't-be-snarky rule, and sticking to it, is a way of globally optimizing for fun—specifically the fun of interesting, curious discussion. That's more important than the sugar rush of being "cheeky".
Here's a thought experiment for you: pretend the return type is something other than 'error': result, statuscode, responseContext, anything that doesn't imply failure. Would you then suggest handling that is "noise"?
ETA: "there are countless other [than if err != nil] things one can do with an error value, and application of some of those other things can make your program better, eliminating much of the boilerplate that arises if every error is checked with a rote if statement."[2]
1 https://www.eecg.utoronto.ca/~yuan/papers/failure_analysis_o...
We are unlikely to improve on this until we finally abandon the idea of working directly on a single, plaintext codebase.
If that's not the spooky action at a distance between the place where exception is thrown and where it should be handled, I don't know what is.
And also more recent versions of Java, such as the current one: https://docs.oracle.com/javase/8/docs/api/java/lang/Exceptio...
>Checked exceptions need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.
This is like... the one thing that Java did absolutely right, but for some reason it's also the thing people hate most about it? I've never understood why.
No parametric polymorphism in exception specifications. Like if you have a record parser that invokes a user-defined callback for each record, then you literally can’t type it in such a way as to allow the callback to throw exceptions (that the caller is presumably prepared to handle, having provided the callback in the first place).
To be clear, this is not simple. It seems to me like it’ll quickly go into full-blown effect typing territory, which is still not completely solved today and was in an absolutely embryonic state in the early 2000s.
From a caller's perspective, "res, err :=" and "throws Exception" is the same thing: you need to handle the error on site, which (in contrast to the Go gospel), is not a good thing. In the absolute majority of cases it can't be done sensibly there, you need to pop it up the stack to a place which has more context. Certainly, it's none of a method implementor's business to decide "if a client can reasonably be expected to recover from the error". You know nothing, Jon Snow.
Java has figured out it's a bad concept decades ago, but Go is frustratingly re-enacting Java's early mistake. At least, redeclaring a checked exception was a simpler way of dealing with the error than Go's err ceremony.
They don't, do they? One really nice thing about Go is writing and calling functions that _don't_ return any error. You can be confident that they will not throw any exceptions at all.
You are under an illusion if you think they can't fail.
Is kind of an understatement. If the handling code for that is duplicated as 75% of your code base, there's something wrong with the language. There's got to be some other way than all that noise.
Also, this 75% number sounds made up out of thin air. If your program is doing something non-trivial, it should have far more code doing work than checking errors.
I have not, ever, seen any numbers for exception style or FP style. My perception is that their numbers might be lower, but I have no evidence, and I am not dogmatic about my guess.
int foo(Data *data) {
int error = do_some_io_request(data);
if (error) log_error(error, "Request failed");
return error;
}
For propagating errors up the stack, the ratio is only 50%: int error = foo(data);
if (error) return error;
For the rest of your code, I guess it's domain specific. But most projects should have a significant amount of the codebase doing things with data in memory that can't fail. int handle = open(file, S_IREAD);
if (handle == -1)
return false;
int size;
if (read(handle, &size, sizeof(size)) != sizeof(size))
{
close(handle);
return false;
}
char *buffer = malloc(size);
if (buffer == null)
{
close(handle);
return false;
}
if (read(handle, buffer, size) != size)
{
close(handle);
free(buffer);
return false;
}
And so on, with the number of things you have to clean up growing as you go further down the function.> handled locally
Except in practice, you don’t. You just keep returning the error up the call stack until something handles it at the top, probably by trying again or more likely just logging it.
And to fix this, we introduce 10 places per function to improperly unwind the stack, have a chance at missing an error result, and completely ignoring that fact that anything can fail anyway, even a simple addition. Instead of just writing exception safe code in the first place.
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return err
}
defer w.Close()
if _, err := io.Copy(w, r); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
}You could have smaller discrete functions that abstract the handling of each action a little bit and be more reusable, or is that not possible in Go
These shorter functions need to signal their failure somehow, so calling them looks exactly like the example.
if r, err := os.Open(src); err != nil {
return err
}
defer r.Close()
But then r is not in scope for the r.Close properIf I see that a value can have two or more types then obviously this is "more advanced" (or perhaps better, "more complex") than if it's just one type.
Sometimes this makes things better. Sometimes it doesn't.
> Sometimes this makes things better. Sometimes it doesn't.
Exactly, and that's why you want to have both techniques available, and the data modelling is the judicious interplay of both.
If you'll excuse me I'm going to go walk AND chew gum. Or should that be OR :)
This is just a dismissal instead of an argument, and one that can be applied to almost anything.
This can save a lot on padding, and greatly increase the cache efficiency