Barely and badly: you can ignore the error and it's by far the simplest course of action. Contrary to Haskell where it is easier to propagate it, or Erlang where it is just as easy to turn the error into a fault if you don't want to handle it.
> don't throw the baby out with the bath water!
There is no baby in that bath water.
But hey, I guess I'll give you credit where credit is due: you do know Go's error handling is only an improvement compared to C's, although you still err in declaring it "far better".
> (and the one I picked is merely the worst, your hello world is as disingenuous as Java's with all newlines removed — a single line)
I ran `gofmt` on that code before sharing it, so it conforms to Go's "One True Style". More importantly, my "Hello World" is quite readable, unlike a single line Java "Hello World". This last point in particular addresses the OP's central point: that Go is just as verbose as Java because of the length of "Hello World".
No, it's got nothing to do with sum types, Erlang does not use sum types yet for all the similarity of Go's error handling to Erlang's, Erlang is vastly superior.
> I acknowledged that such enforcement was not exhaustive and that errors could be ignored by the user.
And that's insufficient, errors can almost always be silently ignored by the user (even in languages with sum types which turn errors into faults, you should be able to silence the fault). The issue in Go is that ignoring errors is the simplest thing you can do. And not only that, not ignoring them is a significant step up in complexity and amount of code.
> The issue in Go is that ignoring errors is the simplest thing you can do. And not only that, not ignoring them is a significant step up in complexity and amount of code.
I agree that error handling requires more code, but I disagree that it increases complexity. Most error handling cases I've ever written are just passing them up to the caller:
if err != nil {
return nil, err
}
That is not added complexity IMO---particularly since it is an extraordinarily strong idiom---so I suppose we are at an impasse.Yes, if 10 tokens, 3 lines and a conditional are not added complexity to you, I suppose we are.
> It’s not even enforced error checking, so bad programmers will still be able to write bad code.
Bad programmers will always be able to write bad code. You can't structure a programming language around preventing bad code without completely hamstringing the language.
You can only do this for sufficiently motivated and well-informed programmers.
> The design of Go's error handling mechanism, while not the worst approach imaginable, unfortunately makes the programmer exert extra effort in order to do the "right thing".
Most error handling mechanisms I've seen make doing of of the worst things imaginable (silently throwing away the error) the easiest thing to do. I'm not so sure it's any easier or harder to do the right thing in Go, in the particular context of a highly concurrent system.
EDIT: But I haven't investigated Erlang's mechanism yet. I've heard it's quite good.
I too thought this, until I took a couple of minutes to actually understand how it's supposed to be used. It's a very opinionated setup, but it seems well thought out.
The `:=` does a fair bit for the programmer in cutting back on writing types. For me, the most needed place for type inference would be for writing anonymous closures (like what Rust has). In most other places, I am quite content with writing the types, particularly at the top level.
> Like what’s the point of all the odd rules around := vs =
The former is short-hand for variable declaration with assignment and type deduction, while the latter is just regular assignment. The primary benefit here is type deduction, which partially relieves the lack of type inference.
> and what types you can and can’t assign and pass to functions
Huh?
> And why do you have to declare imports, when the compiler could work them out for you (it’ll even moan if you import something which is not used!)
The compiler absolutely cannot work them out for you. If you have a package `github.com/PeepA/wat` and a package `github.com/PeepB/wat`, how will the Go compiler know which `wat` package to import?
The Go compiler merely moans if an import (or its alias) hasn't been used in the source file that it was imported in. I like this feature, even if it is a mild bother while debugging.
> Hello, world is about 8 lines of code. Camel case! Java rang, wants its boilerplate back.
It's two lines. [1]
> No breakthrough on error handling.
Breakthrough? I'm not sure what you were expecting, but I think the error handling is pretty sane and at least far better than error handling conventions established in C. It could be adjusted if sum types were added to the language, but that has its own trade offs.
If you desperately want exception-style error handling, then you can use panic/defer/recover, but it's frowned upon to overuse it.
> The whole design of GOROOT/GOPATH is completely broken. It’s actually worse than Java’s broken CLASSPATH crap which is some kind of achievement, I guess.
I have the exact opposite opinion. Did you know that you probably shouldn't be setting `GOROOT`? [2] After Go is installed, you just need to set `GOPATH` and add `$GOPATH/bin` to your `PATH`. That's it.
> It’s not even enforced error checking, so bad programmers will still be able to write bad code.
That is true, but Go's compiler forces you to address errors returned by functions that also return another value. You either need to explicitly ignore it or use the variable the error is stored in. (Lest you get an "unused variable" compiler error.) This doesn't cover all cases---like completely ignoring the return value(s) of a function---but don't throw the baby out with the bath water!
[1] - http://play.golang.org/p/ihEoJ0yL9I
[2] - http://dave.cheney.net/2013/06/14/you-dont-need-to-set-goroo...