Skip to content

Comment on Comparing Go and Java, Part 2 – Performance

Comments

Does anyone else find 4 lines of error checking (log and panic) boiler plate code for every line of functional code a bit tedious?

It's annoying, the only thing it has going for it is it's better (in my subjective opinion) than the alternative.

In go you can happily ignore an exception by assigning it to _. That's probably a bad idea for a larger piece of code, but for little scripts, go for it.

I may be already permanently damaged by go. Every time a function can return an exception I have to stop and ask myself: "self, what should you do if this happens?". I'm starting to think it results in better code. Granted, not every shell script/utility benefits from this level of introspection, but that's what python's for I guess.

p.s. Anyone who's had to deal with checked exceptions in java puts up with a crazy about of boiler plate too.

p.p.s. Disclaimer I have been professionally employed writing all languages mentioned above, so hopefully I'm relatively unbiased.

I have invented a remarkable new programming tool that wires up to your chair, keyboard, and 110v AC. It gives you an electric shock every time you complete a line of code, reminding you to stop and think about it. Think of the productivity!

Checked exceptions are indeed obnoxious and a major language design failure, which is why pretty much every modern language since just has plain old (non-checked) exceptions. And even in Java, you can work around the brain damage by wrapping checked exceptions with runtime equivalents in API facades. Exceptions are still incredibly useful, and lack thereof is my biggest complaint about Go.

I'm also disappointed by the convention of capitalizing/lowercasing names to export them or not. Realize that you want to export an existing private method? What's that, your IDE doesn't support refactoring? Get typing, you have a lot of method calls to update.

> I'm also disappointed by the convention of capitalizing/lowercasing names to export them or not.

I think it's fine, personally. It's not overly obnoxious, it gives shape to the code, it avoids the redundancy of an explicit export list (although that also means it's harder to see at a glance what's exported from a module I guess) and it makes sense within Go's habit of mandating formatting, there's no reason not to leverage this mandate.

Coding conventions on steroids, if you will.

Exceptions make it harder to reason about your code's execution path. If you raise an exception, it is often not obvious who in the call stack is ultimately going to catch and handle it. The logic for that can live pretty much anywhere. This is not to mention the try/catch/finally pyramids you get from trying to cope with nested failure cases.

Go uses a well-understood mechanism: return. Control reverts to the caller. It's simple, which was an explicit design goal of Go.

IMHO, if you have a choice between exceptions or not, they just aren't worth the value they deliver. As Josh Bloch says, use them for exceptional circumstances only, to indicate truly exceptional circumstances, such as catastrophic errors.

Re: refactoring: http://golang.org/cmd/gofmt/. Check out the -r option.

In practice, is this really a huge deal? Modulo go fmt, what editor doesn't support multi-file S&R with regex? Isn't this what a compiler is for? All in all, this sounds like bikeshedding about syntax. We're all entitled to our opinions but it's awfully hard to say anything interesting about syntax which has not already been said a bajillion times.

"It gives you an electric shock every time you complete a line of code"

Is that on kickstarter? Put me down for 10!

> It's annoying, the only thing it has going for it is it's better (in my subjective opinion) than the alternative.

Why? There's nothing necessarily wrong about letting it crash, and letting a layer above report the error cleanly. Hell, in Erlang the usage is even to let an other process entirely handle the error.

In fact, my opinion would be the complete opposite of yours: checking every single return value (if only to return it to the caller unaltered) is fine for little script, but it's a bad pattern to need for large pieces of code, it's verbose, redundant and unhelpful.

>There's nothing necessarily wrong about letting it crash

Then do that. You don't have to handle errors, you can ignore them just like you would ignore an exception. The difference is that with an error return value, you are explicitly choosing to ignore it. With exceptions, it is easy to accidently ignore it when you didn't want to.

>but it's a bad pattern to need for large pieces of code, it's verbose, redundant and unhelpful.

Which is an argument for better error handling, not an argument for exceptions. If go had Maybe and Either, there would be no problem.

> Then do that. You don't have to handle errors, you can ignore them just like you would ignore an exception.

No, if I ignore an exception it bubbles up the stack and will either stop the program or find somebody handling it. If I ignore a return value, the program gets into a completely undefined state and will crash later in a completely different place.

Unless there's a way for go to do the same thing as the Erlang pattern:

    {ok, Value} = call(SomeArg, SomeOtherArg).
is there?

I'm not sure what you mean, that is the normal way you do it in go? Multiple return args, one being the one you use, the other being the error condition, which you can ignore by either not checking it, or just outright assigning it to _.

> I'm not sure what you mean, that is the normal way you do it in go?

No, that is the normal way I do it in Erlang (hence the note that this is an Erlang pattern), where there are exceptions (and nobody says there aren't) but most functions tend not to use it and to return tagged tuples: `{ok, Value}` if the call succeeded (or just `ok` if there's no value to return) or `{error, Reason}` if the call failed. Note: lowercase words in Erlang are atoms, you can think of them as interned strings. Words which start with a capital are "variables" (which can't vary, but close enough).

Now the caller can unpack the result:

    case some_call() of
        {ok, Value} -> %% code to execute if the call succeeded;
        {error, Reason} -> %% code to execute of the call failed
    end
this uses pattern matching (on the value being a tuple and having the right atom as its first element) to dispatch each case to the right branch.

But in this sub-thread, we don't want to ignore the error. In Erlang, "ignore the error" is written:

    {ok, Value} = some_call()
this doesn't really ignore the error (and let the function keep running), it asserts that the result of some_call() matches the tuple `{ok, Value}` and faults if that's not correct. The equivalent Go code is what is used in TFA, namely:
    result, err := SomeCall()
    if err != nil {
        panic(err)
    }
and is also equivalent to not catching the exception in Java: it does not let the code keep running.

And my question was thus: is there a way (shorter than the one used in TFAA) to do this, not handle the error but have the error prevent the code from running?

> which you can ignore by either not checking it, or just outright assigning it to _.

No, that leaves the code running in an unknown and corrupted state, I don't consider this acceptable.

>No, that is the normal way I do it in Erlang

It is also the normal way you do it in go. Read the examples, that's exactly why go has multiple return values.

Annoying as hell, that's what you get when you don't support exceptions.

> that's what you get when you don't support exceptions.

That's what you get when you refuse to use them anyway.

Go has exceptions, there's just a dogma about never using them.

You can't really use panics as exceptions. If you do your code will be strange and hard to work with. This strangeness is fine, because they aren't meant to be used like that.

A good rule of thumb is to use panics when it's a programmer error indicating a bug.

Not dogma, just common sense based on experience.

Panic() is for truly irrecoverable exceptional situations where you do not expect the caller to catch it.

You can ignore errors if you want to crash, just like Java.

Yes, exactly. If you find the boilerplate tedious, feel free to ignore errors completely. :) For test code it doesn't matter. It is the equivalent of providing no exception handler, or catching Exception or Throwable and doing nothing.

That is not optimal. It not going to crash in the location of first error. It will plough on a bit and crash somewhere else where the bad object/pointer was actually used to perform an illegal operation.

Something like a 'die on error' option might be useful for trivial scripts and applications.

When prototyping stuff is trivial to have a fail(err) function that panics in case err is not nil.

I sometimes do this, and then remove the fail() function to force myself to properly handle the errors. (Still, often is best to handle the errors as soon as you write the code anyway).

The key is that unlike with exceptions, the fact that you are ignoring the error is explicitly stated in the code, is not something that magically might happen.

And most importantly, errors are part of the documented API, with exceptions it is rarely documented what exceptions a function might throw, much less what exceptions the functions called by that function might throw.

Yes, Go error handling is a bit verbose, but that is a sign of how much better it is than exceptions, without falling into the 'checked exceptions' insanity.

Which is similar to the scenario where you ignore all exceptions and your test script got into a bad state. :)

Much as you could write this in Java:

    catch (Throwable t) {
      throw new RuntimeException("oh noes"); // or whatever
    }
You could write this in Go:
    if err != nil {
      panic("oh noes")
    }

But I can do the former for a whole block of code but have to do the later for each significant line of code.

They are are not the same.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.