top | item 19588728

(no title)

natefinch | 7 years ago

I've been writing Go for over 6 years. Error handling at the source of failure is a feature, not a bug.

Rob Pike's example code is bad and I would not allow it through a code review. Why? It makes the code lie. It appears as though the writes cannot fail, but in fact they can, and later writes become no-ops. On top of that, it means that you lose the context of which write failed. Did you fail writing the header, the body, or the footer? You can't know anymore.

This is like

   try {
        doThing()
        doThing2()
        doThing3()
   catch (Exception ex) {
      // handle
   }
You can't tell what failed. You can't tell what is possible to fail. The handler can't differentiate.

discuss

order

syrrim|7 years ago

Code lies all the time. This is called an abstraction, and is very useful. The abstraction that rob's code provides is that it makes you think of writing not as doing something with the OS, but as merely pushing to a buffer, with flush being the call that actually commits the transaction. Therefore, flush is the call that can fail. Is this literally what is happening? No, but it is a much easier way to think of things, and makes the code more readable.

>Did you fail writing the header, the body, or the footer? You can't know anymore.

You are writing to the same stream in both cases. The error wouldn't be caused by the header or footer data, but by some property of the stream, like it being closed, or the disk running out of space. Knowing whether the failure occurred while you were writing to the header or footer is unnecessary information, and therefore obfuscatory.

ereyes01|7 years ago

I disagree with this rationale, but I'll let someone smarter than me make the argument:

"The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise." - Edsger Djikstra

Or said another way, a good abstraction automates a precise set of tasks instead of adding ambiguity.

jstimpfle|7 years ago

> Knowing whether the failure occurred while you were writing to the header or footer is unnecessary information, and therefore obfuscatory.

So are you making a general case for exceptions or are you just asserting that there exist cases where we don't care where errors happened? There are absolutely such cases. When writing quick and dirty scripts I absolutely value that they crash when they couldn't find a file or similar.

But for more complex systems, I've never found a good use for abstractions. In a complex system, you need to see what happens. Out of sight, out of mind. There is no way building a complex system on top of exceptions and having a good understanding over its failure modes.

I would go further and state that it's extremely uncommon to be able to handle an exception in an appropriate way. The most common use of them is to either 1) handle the error immediately (for example FileNotFound), in which case the exception handling adds just unnecessary syntactical variety. Or 2) just let the program die. Coincidentally, golang has that too.

There may be rare situations where you can handle a series of statements with a single exception handler, like several write-file statements in a row or such, and that might seem cleaner than repeating the error handling. But actually these cases are just a code smell. You could rewrite them using a for loop and factor the repetitions as actual data, adding the error handling only once (inside or after the for loop). In other words, exception handling is just a way to paper over that code smell. It makes hiding bad code easier, so leads to bad code.

crimsonalucard|7 years ago

That type of attitude I would not allow through an employment review. Too many programmers share varying opinions on style. Enforcing your design principles onto others during code review is not only arrogant but you can't prove in any formal way that your "design" is better. Especially on a controversial design such as this.