7 ms·
Ah the old nil values boxed into non-nil interfaces. Even after 8 years writing go code almost every day this still bites me occasionally. I've never seen code
by rowanseymour 1y ago
Ah the old nil values boxed into non-nil interfaces. Even after 8 years writing go code almost every day this still bites me occasionally. I've never seen code that actually uses this. I understand why it is the way it is but I hate it.
- amelius 1y agoI ditched Go after an evaluation years ago. I can remember it was an issue with nil pointers being non-intuitive that turned me off. And exception handling. A pity because the runtime and ecosystem/community seemed pretty good.
- rowanseymour 1y agoIt's fantastic concise language and standard library steered by people who are determined to keep it simple and intuitive... which IMO makes it all the more odd that it has this obvious foot gun trap where `!= nil` doesn't always mean what you might think.
- amw-zero 1y agoThe “simplicity” of Go is just virtue signaling. It has gotchas like that all over the language, because it’s not actually simple.
- LandR 1y agoYep. The lack of features means all the complexity is offloaded to the programmer. Where other languages can take some of the complexity burden off the programmer. Go isn't simple, it's basic.
- amelius 1y agoPerhaps Go is a nice target language for a transpiler, so you could still benefit from the runtime and ecosystem while fixing the bugs in the language itself. Anyone working on this?
- bobbylarrybobby 1y agohttps://github.com/borgo-lang/borgo https://github.com/borgo-lang/borgo
- laumars 1y agoAs someone who's written commercial software in well over a dozen different languages for nearly 40 years, I completely disagree. Go has its warts for sure. But saying the simplicity of Go is "just virtue signaling" is so far beyond ignorant that I can only conclude this opinion of yours is nothing more than the typical pseudo-religious biases that lesser experienced developers smugly cling to. Go has one of the easiest tool chains to get started. There's no esconfig, virtualenv and other bullshit to deal with. You don't need a dozen `use` headers just to define the runtime version nor trust your luck with a thousand dependencies that are impossible to realistically audit because nobody bothered to bundle a useful standard library with it. You don't have multi-page indecipherable template errors, 50 different ways to accomplish the same simple problem nor arguments about what subset of the language is allowed to be used when reviewing pull requests. There isn't undefined behaviour nor subtle incompatibilities between different runtime implementations causing fragmentation of the language. The problem with Go is that it is boring and that's boring for developers. But it's also the reason why it is simple. So it's not virtue signaling at all. It's not flawless and it's definitely boring. But that doesn't mean it isn't also simple. Edit: In case anyone accuses me of being a fanboy, I'm not. I much preferred the ALGOL lineage of languages to the B lineage. I definitely don't like a lot of the recent additions to Go, particularly around range iteration. But that's my personal preference.
- bobbylarrybobby 1y agoYou are comparing Go to Python, JS, and C++, arguably the three most complex languages to build. (JS isn't actually hard, but there are a lot of seemingly arbitrary decisions that have to be made before you can begin.) There are languages out there that are easy to build, have a reasonable std lib, and don't offload the complexity of the world onto the programmer.
- laumars 1y ago> You are comparing Go to Python, JS, and C++, arguably the three most complex languages to build. No, I'm comparing to more than a dozen different languages that I've used commercially. And there were direct references there to Perl, Java, Pascal, procedural SQL, and many, many others too. > There are languages out there that are easy to build, have a reasonable std lib Sure. And the existence of them doesn't mean Go isn't also simple. > and don't offload the complexity of the world onto the programmer. I disagree. Every language makes tradeoffs, and those tradeoffs always end up being complexities that the programmer has to negotiate. This is something I've seen, without exception, in my 40 years of language agnosticism and part-time language designer.
- ignoramous 1y ago> because it’s not actually simple Cue Rich Hickey's Simple made Easy: https://www.youtube-nocookie.com/embed/SxdOUGdseq4 https://www.youtube-nocookie.com/embed/SxdOUGdseq4 / https://ghostarchive.org/varchive/SxdOUGdseq4 https://ghostarchive.org/varchive/SxdOUGdseq4
- ignoramous 1y ago> And exception handling If you read & write Go regularly, the rather verbose error handling simply fades into the background. That said, errors in Go don't really translate to Exceptions as generally thought of; panic, however; may be does. Making changes to error handling wasn't for the lack of trying, though: https://news.ycombinator.com/item?id=44171677 https://news.ycombinator.com/item?id=44171677 > issue with nil pointers This is why most APIs strive for a non-nil zero value, where possible, as methods (on structs) can still dictate if it will act on a pointer. Though, I get what you're saying with Go missing Optional / Maybe / ? operator, as the only other way to warn about nil types is through documentation; ex: https://github.com/tailscale/tailscale/blob/afaa23c3b4/syncs/syncs.go#L148 https://github.com/tailscale/tailscale/blob/afaa23c3b4/syncs... (a recent example I stumbled upon). Static code analysers like nilaway (https://news.ycombinator.com/item?id=38300425 https://news.ycombinator.com/item?id=38300425) help, but these aren't without false positives (annoying) & false negatives (fatal).
- peterashford 1y agoI read & write Go regularly, and I hate its error handling intensely
- aatd86 1y agoYes, that'a bit too late after ten+ years perhaps but I wished we had a nil type and checking whether the interface is empty was a type assertion. In all other cases, like any(2) == 2, we compare the values. Then again that would mean that the nil identifier would be coerced into a typed nil and we would check for the nilness of what is inside an interface in any(somepointer) == nil. wrt the current behavior, it also makes sense to have a nil value that remains untyped. But in many other cases we do have that automatic inference/coercion, for instance when we set a pointer to nil.(p = nil) That's quite subtle and that ship has sailed though.
- rowanseymour 1y agoAgree the ship has likely sailed, but if it could be addressed wouldn't it be nice to remove nil value interfaces altogether? Maybe start by letting new interface types declare/annotate that they don't box nil values? Then one day that becomes the default. Oh well.
- aatd86 1y agoOh that's probably doable. Introducing something like this is a bit orthogonal to the point above, but yes. It's not straightforward but probably something that will be considered at some point I reckon when thinking about making union interfaces first class. That will require to track a not nil typestate/predicate in the backend, something like that I guess.
- rowanseymour 1y agoHaving pondered on a bit more.. I think it's the struct that would declare that it's not usable as nil, and that in turn would tell the runtime not to box it if it's nil. That would also help the compiler (and copilot etc) spot calls on nil pointers which will panic.
- aatd86 1y agoBut that information disappears when you assign to an interface variable/container that is nillable. It requires an assertion to recover the info about the value inside the interface being not nil. basically `if v.(nil){...} creates two branches. In one we know v is not nil (outside the if block) and it can therefore be assigned to non nillable variables so to speak...
- kitd 1y agoThe advice I've read (and follow) is always to return values, not interfaces, from functions and test for nil against them. That IME tends to nip the majority of nil interface problems in the bud.
- YesThatTom2 1y agoExactly. I find that people try to use interfaces like they’re using an OO language. Go is not OO.
- leetrout 1y agoReturn concrete types, accept interfaces. Returning interfaces hides behavior and hampers evolution; accept interfaces so callers can swap implementations. For testing, mock the dependency, not the return value. Loudest arguments against returning concrete types were on the terraform core team and the excuse was it makes testing easier. I disagree.
- lenkite 1y agoThis advice of "returning concrete types" is in most cases a horrible anti-pattern that prevents evolution due to lack of information hiding. It has been also deliberately broken in the standard library in several places. This "advice" cannot be generically applied. Places where it is has been deliberately broken: net.Dial (Conn, error) image.Decode(r io.Reader) (Image, string, error) sha256.NewXXX() hash.Hash flate.NewReader(r io.Reader) io.ReadCloser http.NewFileTransport(fs FileSystem) RoundTripper Regarding `os.File`, the Go team even said: “If we were starting from scratch, we might do it differently.” That’s why Go added abstractions later like fs.FS and fs.File. embed/fs.Open again deliberately breaks this. Whereas consider its counterpart net.Conn. net.Conn is one of the most successful interfaces in the Go standard library. It’s the foundation of the net, net/http, tls, and net/rpc packages, and has been stable since Go 1.0. It didn't need a replacement fs.Fs. If you will always only ever have one implementation in absolute permanence and no mocking/fake/alternative implementation is ever required in eternity, return a concrete type. Otherwise, consider whether returning an interface makes more sense.
- thomashabets2 1y agoBe prepared to be called a newbie for criticising typed nils. My post https://news.ycombinator.com/item?id=44982491 https://news.ycombinator.com/item?id=44982491 got a lot of hate from people who defend Go by saying "so just don't do that!", and people trying to explain my own blog post to me.