Live data from Hacker News

Go generics are not bad

lemire.me

261–270 of 305 posts

Re: Go generics are not bad

#261
post #162

Earlier quoted context omitted.

Can’t hate on generics, so we need to find something else. Go takes away type system fidget spinners and leave devs with nothing to do but the actual job, which they hate, and thus they hate Go.

That’s actually a brilliant assertion (sorry for the crap pun). The majority of developers I work with are only aware of the success paths of their code. That means we’re knee deep in exception corpses because they avoided doing part of their job. Go makes them deal with it.

When we were rewriting a module in our backend from PHP to Go, explicit error handling helped us find and fix bugs due to edge cases we weren't even aware of. The usual idiom in PHP was to just catch exceptions at the top level and show an error (I think that's what exception handling usually devolves to). Since in Go we were explicitly handling errors at every level in the chain, it forced us to reason about error conditions more carefully and made us more aware of potential edge cases, which didn't happen when we were writing in PHP because you only saw the happy path when looking at the code (i.e. little mention that something can go awry because exceptions are hidden from control flow). I remember in one such case, we found out you couldn't just bubble up an exception, you had to process it immediately, otherwise data could end up in an inconsistent state.

However, repetition can introduce bugs, too, for example a very common mistake is to write this:

  if err != nil {
    return nil
  }
instead of this:

  if err != nil {
    return err
  }
which is pretty serious because you essentially ignore the error.

This particular pattern is so common your brain often ends up completely skipping it and it's pretty hard to detect during code review.

Re: Go generics are not bad

#262

What I still don't understand is why golang has exceptions for "language constructs" like make() and append()... while those are unimplementable in golang. It's pretty annoying that golang embraces static and functional patterns in its core, but it doesn't even have map/filter/reduce/forEach as generic implementations for all data types. The lack of a "new" keyword and default values for struct's property syntax is a…

> but it doesn't even have map/filter/reduce/forEach as generic implementations for all data types. Generics landed in the latest stable version, 4 months ago. Before doing too many too early additions in the stdlib they want to observe how generics are used in the wild. I don't have the link handy, but it was mentioned in one of the generics discussions (issue on GitHub). In these cases (another case was error wrapp…

>they want to observe how generics are used in the wild. I don't have the link handy, but it was mentioned in one of the generics discussions (issue on GitHub).

The rationale can be found here:

https://github.com/golang/go/issues/48918

Re: Go generics are not bad

#263

Earlier quoted context omitted.

There is no “inner/outer nil”. People just need to understand how interfaces work (they’re a reference type and sometimes they reference another reference type, so just like any language with nullable pointers/references, either the pointer or the pointee can be nil). 101 level stuff. There are valid criticisms of Go (I’m not a fan of nil in the first place, for example), but this inner/outer nil myth needs to die.

Sure, if any method anywhere takes a pointer receiver, then anyone can declare an interface that matches that method, and that interface is (among other things) like an Option > in Rust or a ??T in Zig. It's weird to post "There is no inner/outer nil" since the most straightforward interpretation of that claim is that there is no None/Some(None) distinction, and this is straightforwardly not true. For some reason, mo…

> the most straightforward interpretation of that claim is that there is no None/Some(None) distinction, and this is straightforwardly not true.

That’s not the most straightforward interpretation, since it would invalidate the original criticism that I responded to (that Go more-or-less uniquely requires you to check inner/outer nil).

> For some reason, most languages do not encourage the users to create types that are similar to Option>.

Lots of languages “encourage” references to reference types (including Rust), but many of the ML family including Rust have non-nilable references. Go’s error (and that of many others including Python, Java, C, JS, etc) isn’t that it supports/encourages references to references, but rather that all references are nilable.

Re: Go generics are not bad

#264
post #260

Earlier quoted context omitted.

Apologies, I mean that, if you came up with some kind of pathologically bad architecture, it could encounter the same failure mode when trying to add a parameter to a function (like, the callers need to add a parameter, and their callers, etc). But as you note, adding parameters to a function is routine, and I've never heard of this happening. I've definitely added parameters in a way that was tiresome and required m…

I do agree with that, and with the implication that one shouldn't add generics (or other abstraction) where they don't provide enough value for their costs. But I'm confused because your example (adding a generic parameter to a function) seems to be an example of adding abstraction to code that did not previously have enough abstraction.

Yeah for sure. If we need more abstraction we need it, generics are just really, really abstract. I'm just saying generics should be a last resort. And if you find yourself with generics all over the place, you might take a step back and ask, did I make a bad architectural decision that will blow up in my face later? Can I do a medium sized refactor now to save myself a massive refactor later?

Coming from Python, I had a bad habit of premature abstraction. In Python, it's easy to be very generic at very little cost (not necessarily using generics - they exist in Python, but they're not "real" since Python is gradually typed). I thought of keeping things generic as "designing for expansion". Then I encountered the problems I've been describing, small refactors would turn into giant ones, and it was entirely unsustainable.

When I asked for advice about this, what I got was pretty much, "Oh yeah, that'll happen. Just don't use generics if you can get away with it." Initially that felt like a nonanswer to me, even a brush off. But as I matured in Rust I realized the advice was spot on, and that I had been abstracting prematurely.

I've seen techniques that can use generics well and actually make coupling looser, and that's awesome, and I don't mean to suggest that one should never use generics. I acknowledge I got into trouble by _misusing_ them. I'm just saying it's an unwieldy tool for special situations. It will rapidly expend your complexity budget.

The original context I was responding to was something like, someone says, generics are great until you get in generic hell, and then someone was like, generics seem fine to me. And I just wanted to explain how one gets into generic hell.

Re: Go generics are not bad

#265
post #204

Earlier quoted context omitted.

Your statement may be correct, and your book may be excellent. But insulting a free knowledge sharing article while posting your own site which is laden with 'buy buy buy' is rather poor taste, IMO.

You don't need to buy it. It is free for reading online, and most contents in my website are free. "insulting"? That is a too heavy word.

I rather agree, and must apologize as English seems to be slipping from me lately. I spent some time trying to think of the right word, but ended on insult, debating whether that was too negative of a word to use. Perhaps dismissive would have been better?

Re: Go generics are not bad

#266

What I still don't understand is why golang has exceptions for "language constructs" like make() and append()... while those are unimplementable in golang. It's pretty annoying that golang embraces static and functional patterns in its core, but it doesn't even have map/filter/reduce/forEach as generic implementations for all data types. The lack of a "new" keyword and default values for struct's property syntax is a…

What is for loop fatigue?

Seems like a fake problem that one would only feel if they buy too much into functional programming.

Some ideas in fuctional programming are useful, but overall, for loops are much more preferable to map/filter/reduce.

For loops are much more easily amenable to incremental changes.

"reduce" is particularly bad. It obfuscates control flow for no benefit at all.

Re: Go generics are not bad

#267

Earlier quoted context omitted.

Any moderately complex app is made up of so many moving pieces, I'm amazed that you can pinpoint those failures specifically to the web frontend piece.

I can't find what I wrote at the time, but there were multiple UI/UX issues and logic bugs.

In any case, the point I was making was that languages from academia potentially could scale up in high-velocity and larger teams.

Re: Go generics are not bad

#268
A lot of folks have commented on this being a cherry-picked example, but I haven't yet seen someone link to a set of examples that show the opposite: pathologically bad performance with Go's generics.

https://planetscale.com/blog/generics-can-make-your-go-code-... is a much more in depth dive into how Go's generics work that shows some of the easy cases, like this article, but also the more general cases.

Re: Go generics are not bad

#269

Earlier quoted context omitted.

That's not theoretical soundness, that's actual correctness. I'm mostly talking about things like io monads.

If your code can run IO operations synchronously and branch on the result of previous operations, you've already got an IO monad. The point of being explicit about that is that it allows you to check that a given piece of code can't do those things. Keeping database calls out of the render loop, for instance, is very much a matter of actual correctness.

In most languages with an io monads your power of proving that x can or can't be done comes at the cost of hiding the business data inside of the monad type (that wraps it) which severely impacts debuggability and in many cases, code legibility and understandability of the data you care about -- because there might be a business logic error elsewhere that you need to track down that no amount of formal correctness will save you from.
Post reply on HN