6 ms·
I'm no Go expert, so the following may just come from having not used it enough. The two things that shocked me out of continuing with Go were exception handli
by erikb 9y ago
I'm no Go expert, so the following may just come from having not used it enough.
The two things that shocked me out of continuing with Go were exception handling and package management.
Exception handling is basically not implemented. Instead the Go developers dediced to add half a step from return codes towards exceptions and work with that. And now the community seems to have decided to just reraise everything that comes their way. Nobodoy seems to see that such a simple basic feature shouldn't require a piece of code, it should be done automatically by the language. And the community doesn't seem to see why it's nearly impossible to debug. I really hope that Go developers have some secret tools I just don't know about, because there are no stack traces, and on the abstraction level of a user getting an error from an underlying framework printed out doesn't really help at all, especially when the code seems to continue after printing the error (making it a bug, by reporting a warning as an error). My favorite example is kubernetes helm reporting a connection error in my job's infrastructure, every time you use it, but after that error it actually switches from IPv6 to IPv4 and just works. But still it feels good about reporting that error. wtf.
The next thing is package management. I had high hopes here after going through the trouble of working on that topic in 2012 when the Python community was developing its package management. I mean if you don't add that on top of an existing world, but create it from scratch, there is a chance to just do it right and not have to bother with all the pains of legacy systems, right? Well, Go decided it doesn't need versioning on imports. Just put the Github link there, don't even choose a branch. How can any code ever get finished that way? My best guess is that now they have to develop stuff on top of that already-born-as-legacy system and also try to integrate that with their core. Sad.
I really hope someone can add some corrections to this view.
- gjem97 9y agoThe only time I miss exceptions is when I really need non-local return. Stack traces are available, see [1]. Needing non-local return really turns out to be the exception though (sorry). I find I want it when writing recursive descent parsers, and some other deeply nested control structures. In those cases, the panic/recover mechanism can be helpful. But for everything else, returning an error (using a standard error interface) works just fine. As far as package versioning, what we ended up doing is vendoring everything into our source-control system. This fits our use better all around. For our production systems, we don't want inequality bounds on our library versions. We have tested our system with version X, we only want to ship with version X, not, X.1 or X.2. Vendoring allows us to pick a specific version. Admittedly, this makes more sense in the Go world, where executables tend do be statically compiled. Vendoring makes it trivial to track local changes to code in our dependencies, and ship them upstream when appropriate. It makes new developer setup simpler. Finally, the Go toolchain has support for vendored code that makes the setup relatively painless. And near instant compilation means that you don't have to wait very long for your dependencies to compile. [1] https://golang.org/pkg/runtime/debug/#PrintStack https://golang.org/pkg/runtime/debug/#PrintStack
- erikb 9y agoThanks for providing additional information. What do you do about the user of your tool having another point of view than you? For instance look at the helm example I provided. You may start a network call to IPv6 first, and when it fails (with an error, which is correct) you continue with IPv4. If either one of these works for your user there is no error. At best there is a warning scenario where you want to educate him about finally switching to IPv6. But if you report a connection error in almost any scenario that would be a bug in your code, because the connection didn't error. You did a test which failed which was expected by your code and handled correctly.
- stubish 9y agoPackage management is being worked on as part of the hopefully-to-become official 'dep' tool, which will prefer semantically versioned releases over pulling trunk. Things might be good here in 12-18 months. Its been acknowledged as a problem by the language developers. Error handling is still exactly where you remember. Most of your code is still 'if err := p.something(); err != nil { return fmt.Errorf("I need to annotate the error so I know where this happened: %v", err) }' Yes, you still tend to get regurgitated and poorly applied rationalizations if you bring up limitations. However, the language designers have been soliciting use cases and requests for improvements for Go 2.0 via their wiki. It does involve educating and convincing the community, which is a hard road and likely only to get submissions from the echo chamber, so I'm not holding out much hope beyond something like generics; it would be a losing battle to pitch error handling changes to true believers. Time will tell if this is a good approach or not, as opposed to messier changes like you see in something like Python.
- nicpottier 9y agoI'm about 8 months in to using Go for a few largish projects and I'd say these are probably the two biggest things I still struggle a bit with. (not generics as others seem to obsess about) On errors, I'm really of two minds. In a way, it is a lot like how Java started with checked exceptions, it forced you to deal with the error. But at some point most people decided that was annoying and switched to runtime exceptions for everything, which while requiring a lot less code, still led to errors often bubbling up all the way to the user. I think checked is the right thing, but it does require developers to be thoughtful and not just throw errors upwards. If you accept that checked is the route you want to go, I don't find Go's use of error values worse than exceptions. On the package management front, I think the current best practice for projects that must not break is to vendor your dependencies. Go is working towards having better tools to allow you to specify SHA's for your dependencies easily but we aren't fully there yet. While it took a bit to wrap my head around using `govend` to vendor my dependencies, in the end it really hasn't ended up being a big pain point in practice. I also never have to worry about a dependency either disappearing or pulling a trick of shipping the same version # with different code. (or having the repo/package manager be down, which anybody who has shipped a lot of code will tell you has happened) So yes, I agree these are both, weird, but they aren't deal breakers. For our particular application, I really love Go, more so than I have any other language in recent memory.
- eweise 9y agoChecked exceptions suck. Every method has to explicitly throw them up to a higher level where they can be handled causing tons of useless boilerplate. Its much better to just let unchecked exception bubble to a higher level of the app. This is a best practice in java so letting the exception bubble all the way back to the user is just poor programming.
- erikb 9y agoI have to google what the "checked" actually means. But in Java you can throw parent classes, in Python you don't need to declare them at all. And honestly having a bad implementation is still better than no implementation. The main point here is to get correct, helpful error reporting so people understand what's going on. If I spend another 4 hours debugging an error message that was actually just a warning and not a biggy at all I'm going to throw up.
- spyspy 9y agoIf you want a stack trace with your errors you can use errors.Wrap()[1]. This repo should replace the stdlib errors package. 1. https://github.com/pkg/errors/blob/master/errors.go#L180 https://github.com/pkg/errors/blob/master/errors.go#L180
- erikb 9y agoThanks! Can this also be added to already compiled tools somehow? E.g. kubernetes uses a lot of go components, but of course I only receive them in compiled form.
- literallycancer 9y ago>especially when the code seems to continue after printing the error (making it a bug, by reporting a warning as an error). Maybe I'm misunderstanding something, but if you call a function and it returns an error, it's your responsibility to ensure that you handle it. Generally you'd assume any data returned by the same call that returned the error is useless (except stuff like EOF, in some cases?).
- erikb 9y agoYes, exactly. That's what one would assume. I don't understand why a whole language community decided to not handle any errors and instead just bubble them up without filtering, without adding more notifications about the context like log messages, without trying to recover. Why would they expect a user to know their whole dependency tree and all their error messages.
- _ph_ 9y agoI am not aware that they did. Most good Go code does not do this. That there exist some badly written Go programs does not contradict this. Go functions that can fail, should return an error as part of the function returns. Like with exceptions, you can decide to ignore any error or properly handle it. Even with checked exceptions, you can have an empty "catch", which means you are just ignoring it. So the difference is rather syntactical. And like with exceptions, proper error handling is entirely the responsibility of the programmer who writes the error handling code. The Go version of error handling requires less typing overall, and makes it a little bit easier to ignore errors. Unhanded exceptions on the other side can unwind the stack arbitrarily high up until they do eventually find a handler - but it is not said, that this handler can properly handle the exception, as it might be separated to far from the error source.
- vram22 9y ago>The Go version of error handling requires less typing overall Do you mean as compared to using exceptions in a language like Python or Java? If so, can you give an example of code that does the same task, in both types of languages, to make this more clear? Thanks.
- komuW 9y agoPackage management is currently been worked and the tool[1] is stable now, at least the user facing parts. Here's where things stand as of a few days ago[2] There has been efforts to use dep in kubernetes[3][4] 1. https://github.com/golang/dep https://github.com/golang/dep 2. https://sdboyer.io/dep-status/2017-08-17/ https://sdboyer.io/dep-status/2017-08-17/ 3. https://github.com/golang/dep/issues/110 https://github.com/golang/dep/issues/110 4.https://github.com/golang/dep/issues?q=is%3Aissue+kubernetes+is%3Aopen https://github.com/golang/dep/issues?q=is%3Aissue+kubernetes...
- vorg 9y agoThe go dep tool has been worked on and promoted by some of the Go committers over the past year or so, but we don't really know if the Go Language designers with the clout to change the toolset have been sold on the idea yet, or if it will be vetoed later on. Some of them haven't said anything in support of it (or against it), so go dep is still really just of proof of concept.