Live data from Hacker News

Go subtleties

harrisoncramer.me

71–80 of 190 posts

Re: Go subtleties

#71
post #61
post #57

Earlier quoted context omitted.

> In all other cases, like any(2) == 2, we compare the values. But any(nil) == nil returns true like you'd expect. The reason that any((*int)(nil)) == nil is false is the same reason that any(uint(2)) == 2 is false: interfaces compare values and types .

that's another thing that makes it difficult to fix. Same thing here. 2 is an untyped constant so it should have returned true. (even if int is the default picked on short assignment) any(uint(2)) == int(2) should return false indeed however.

Untyped constants deserve an entry of their own in a list of the language's subtleties, that's for sure.

Importantly, untyped constants don't exist at runtime, and non-primitive types like interfaces aren't constants, so any(uint(2)) == 2 can't behave the way you want without some pretty significant changes to the language's semantics. Either untyped constants would have to get a runtime representation--and equality comparisons would have to introduce some heavyweight reflection--or else interfaces would have to be hoisted into the constant part of the language--which is quite tricky to get right--and then you just end up in a situation where any(uint(2)) == 2 works but x == 2 doesn't when x turns out to be any(uint(2)) at runtime.

Re: Go subtleties

#72

Earlier quoted context omitted.

It'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.

The “simplicity” of Go is just virtue signaling. It has gotchas like that all over the language, because it’s not actually simple.

As 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.

Re: Go subtleties

#73
post #53

> The wg.Go Function > Go 1.25 introduced a waitgroup.Go function that lets you add Go routines to a waitgroup more easily. It takes the place of using the go keyword, [...] 99% of the time, you don't want to use sync.WaitGroup, but rather errgroup.Group. This is basically sync.WaitGroup with error handling. It also has optional context/cancellation support. See https://pkg.go.dev/golang.org/x/sync/errgroup I know it…

The extended standard lib is pretty great, but definitely can't keep the Go compatibility promise, so it's good that it's separate.

Re: Go subtleties

#74
post #64

Earlier quoted context omitted.

> Did I add explicit checks for all the errors my function calls might return? You can easily check this with a linter. > Are all of my resources (e.g. file handles) cleaned up properly in all scenarios? Or did I forget a "defer file.Close()"? (A language like C++ solved this problem with RAII in the 1980s) You can forget to use `with` in Python, I guess that's also C now too eh? > Does my Go channel spaghetti proper…

>Did I add explicit checks for all the errors my function calls might return? You can check anything with a linter, but it's better when the language disallows you from making the mistake in the first place. >You can forget to use `with` in Python, I guess that's also C now too eh? When using `with` in Python you don't have to think about what exactly needs to be cleaned up, and it'll happen automatically when there…

This seems like a judiciously designed API to me.

You don't need to check if err was nil before calling resp.Body.Close()

https://pkg.go.dev/net/http#Get

> When err is nil, resp always contains a non-nil resp.Body. Caller should close resp.Body when done reading from it.

https://pkg.go.dev/net/http#Response

> The http Client and Transport guarantee that Body is always non-nil, even on responses without a body or responses with a zero-length body. It is the caller's responsibility to close Body.

Calling http.Get() returns an object that symbolises the response. The response body itself might be multiple terabytes, so http.Get() shouldn't read it for you, but give you a Reader of some sort.

The question then is, when does the Reader get closed? The answer should be "when the caller is done with it". This can't be automatic handled when the resp object goes out of scope, as it would preclude the caller e.g. passing the response to another goroutine for handling, or putting it in an array, or similar.

Go tooling is more than happy to tell you that there's an io.ReadCloser in one of the structs returned to you, and it can see that you didn't Close() it, store it, or pass it to somewhere else, before the struct it was in went out of scope.

Re: Go subtleties

#76
post #6
post #5

Great list of why one can love and hate Go. I really did enjoy writing it but you never get the sense that you can be truly certain your code is robust because of subtle behaviour around nil.

I guess as a corollary, Go really rewards writing the dumbest code possible. No advanced type shenanigans, no overuse of interfaces, no complex composition of types. Then you will end up with a very fast, resource light system that just runs forever.

You could say the same thing about any language. Writing "dumb" code is easier to understand especially in the small. But Go with function that take functions and return functions, channels and generics, can quickly look as complex as other languages.

Re: Go subtleties

#77
post #40
post #39

Earlier quoted context omitted.

Yep. 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.

Perhaps 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?

https://github.com/borgo-lang/borgo

Re: Go subtleties

#78
post #72

Earlier quoted context omitted.

The “simplicity” of Go is just virtue signaling. It has gotchas like that all over the language, because it’s not actually simple.

As 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 easi…

You 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.

Re: Go subtleties

#79
Great list! Reminds me to check out more of the new stuff in 1.25.

The one thing I wish Go had more than anything is read-only slices (like C#).

The one thing I wish more other languages had that Go has is structural typing (anything with Foo() method can be used as an interface { Foo() }.

Re: Go subtleties

#80
post #71
post #61

Earlier quoted context omitted.

that's another thing that makes it difficult to fix. Same thing here. 2 is an untyped constant so it should have returned true. (even if int is the default picked on short assignment) any(uint(2)) == int(2) should return false indeed however.

Untyped constants deserve an entry of their own in a list of the language's subtleties, that's for sure. Importantly, untyped constants don't exist at runtime, and non-primitive types like interfaces aren't constants, so any(uint(2)) == 2 can't behave the way you want without some pretty significant changes to the language's semantics. Either untyped constants would have to get a runtime representation--and equality…

Not sure that reflection would be needed. They are exclusively on the RHS. But you're right. They would have a sort of type of their own instead of basically being int under the hood. type conversions do not require reflection. Or maybe you are thinking about something I have overlooked? In any case, not very likely a change anyway.
Post reply on HN