Hacker News new | ask | show | jobs
by moe 3940 days ago
Error handling does obscure the business logic when, as in Go, nearly every operation needs to be followed by a boilerplate guard clause.

Other languages (e.g. Java) have solved this problem quite elegantly over a decade ago with the introduction of Checked Exceptions[1].

Since this is the pattern that every Go program ends up emulating anyway, Google could save everyone a lot of work by just baking it into the language.

In the eternal words of Larry Wall:

  The computer should be doing the hard work.
  That's what it's paid to do, after all.
[1] https://en.wikipedia.org/wiki/Exception_handling#Checked_exc...
3 comments

I think almost 50% of my Go code is checking for errors. Every time I see:

foo, err := somebullshit()

if err != nil {

  // bleargh
}

I feel as though I've experienced a very slight but irrevocable grand mal seizure.

Sorry, but if you have every operation followed by a guard clause you are doing it wrong.

For example,

https://blog.golang.org/errors-are-values

That's the right approach, but it's still decades behind what a decent programming language would give you for free. You have to copy-paste that "errWriter" definition for every possibly-error-raising method that you want to call, because go has no generics. If you want to write to two different writers, let alone a list of them, then you have to go back to the function-and-variable version, which is very boilerplatey and impossible to reason about when refactoring.
This just shows that, to avoid having do-and-check after every operation, the provider needs to invent several idioms because it's tedious to the consumer.

But now the consumer needs to know multiple idioms and recognise when they are being used. And the reader of the consumer code needs to know that the normal `err != nil` idiom is not being followed. All of which requires everyone involved to do more reading of source code than a "throws" line.

How is this simpler than having a single, universal concept of exception handling?

Your link describes (and encourages) exactly the laborious emulation of Checked Exceptions that I was talking about in my above comment.

It doesn't make the guard clauses disappear. It merely forces the programmer to manually aggregate them at a higher level with even more boilerplate code.

If my guard clause is doing the same thing for 20 lines, it increases readability. Could be an optimization point too!
> https://blog.golang.org/errors-are-values

The "helper" function there introduces 10 lines of code (per function in which you want it), and doesn't even work if you call anything besides that one `write` in sequence.

Mostly agree, but I'm sure you must have meant UN-checked exceptions :-)

  if ( somethingICanDoNothingAbout)
      { throw new UncheckedGameOverException( "You're screwed!") }
...

  // top level event/request/message handler, many layers up
  catch ( Throwable e)
      { log.error( "Game over:", e); ... }

(I'm with the Anders/C# camp on this one, as much as I dislike MS otherwise)
No, I mean Checked Exceptions. Unchecked Exceptions are the devil and should be abolished.

The inclusion of Unchecked Exceptions is one of the biggest warts on the Java language.

Unchecked exceptions are for programmer errors. They are an important part of the language since humans (who write the programs) are error prone. They are unrecoverable by nature. Checked exceptions should only exist in situations where the caller can definitely recover from them. Arguably almost nobody does the latter correctly when choosing checked exceptions and the consensus is, as a result, this is the kind of exception that should have been omitted from the language.
Unchecked exceptions are for programmer errors. They are an important part of the language since humans (who write the programs) are error prone.

I should clarify: The wart is that programmers can introduce their own unchecked Exceptions. This should not be allowed since, as you say, unchecked Exceptions only make sense for unrecoverable runtime errors.

Checked exceptions should only exist in situations where the caller can definitely recover from them.

False dichotomy. Unchecked exceptions become part of the callee's method signature, i.e. the contract that the caller must fulfill.

Arguably almost nobody does the latter correctly when choosing checked exceptions

Baseless claim. I see many people using Exceptions correctly and elegantly, for error handling and flow control.

and the consensus is

Opinion != consensus.

There are times when Go (et al) style multiple return values would better serve a function than exceptions.

For example, a function to parse a string into a number should return both a number-reference, and a flag of some kind (or maybe just an Option). Crap input should not generate an exception like out-of-memory or an I/O error. Trying to dereference the number w/out checking to see if the string was parseable COULD generate a not-mandatory-to-check exception, though.

I guess there is a certain amount of tension between the idea that functions/methods should document the kind of errors/exceptions they might have, and between the annoyance that is all the crappy do-nothing catch clauses that turn around and re-vomit wrapper exceptions that ultimately land in a "well, it didn't work" log message and punt, rather than really "handling" the original exception in any way, shape or form.