timmytokyo 9 days ago

Your example of go code that's harder to read is iterators, and I agree with you. There's no denying that code like this places a high cognitive load on the reader:

  func (al *AssocList[K, V]) All() iter.Seq2[K, V] {
    return func(yield func(K, V) bool) {
      for _, p := range al.lst {
        if !yield(p.key, p.value) {
          return
        }
      }
    }
  }
But the code that actually uses iterators is in my opinion more readable than its non-generic counterpart. So it's really a question of how often you're expected to write (or read) iterators. And I don't expect that most programmers will be writing (or reading) iterators that often.
  • timmytokyo 9 days ago

    On further reflection, I think what makes this example particularly difficult to understand is not so much its use of generics, but the way it uses functions. It's a function that returns a function that takes another function as an argument. The generic [K,V] type arguments are actually pretty straightforward.

  • chamomeal 9 days ago

    I often feel this way about heavy use of typescript generics. The more you lean into the crazy (and awesome) world of generics, the more inscrutable the code becomes to anybody who isn’t a generics wiz. It’s really like an extra language stacked on top of JS. I’ll come back to code I wrote a year ago, and it’ll take me a full day to figure out the types.

    But the simplicity of using a library or set of functions that have really nice generics? So awesome. The intellisense and type errors alone can almost be a decent form of documentation.

    The source becomes hard and weird to change, but the end result is a very nice DX

  • Cthulhu_ 9 days ago

    I'll admit I've only ever done one serious Go project but I've thankfully never felt a need to use generics, before generics there were the builtin list and map types that were themselves generics.

mervz 9 days ago

Meanwhile, error handling still can't get any sort of syntactic sugar

  • randomdata 9 days ago

    That's because nobody has yet solved the side effect problem of the sugar.

    All the proposals that have ever been given have ultimately boiled down to essentially `return err`, which, while suitable for meme comments on an internet forum, cannot be used in a real production application for many obvious (and some not immediately obvious) reasons.

    At least under the direction of rsc (the new leadership is still settling into the role so that is less clear), the will to add such sugar was there if a good solution was found. But the solution has yet to be found.

    • jimbokun 9 days ago

      I don't know what the syntax should look like.

      But the most common pattern is a sequence of calls to functions that return an optional error plus the happy path value, followed by a short circuiting check of the error, followed by a call to another function with the happy path value as an argument. It's very common to have a chain of these kinds of calls making up the body of a function.

      It seems like "return err" is very useful for this pattern, if I understand you correctly. A function returning the error from the first call it makes that fails, or the happy path value if all the calls succeed. Seems like it should be possible to bake that pattern into the language, but its tricky doing it a way that doesn't obfuscate the underlying semantics, which is very important to many Go developers.

      • randomdata 9 days ago

        > I don't know what the syntax should look like.

        I'm not sure the syntax is all that significant. There have been numerous proposals, but the syntax was never the reason for rejection. It is that the entire concept is unusable in the state that it is understood.

        That's not to say the problems can't be solved, but nobody has yet.

        > It's very common to have a chain of these kinds of calls making up the body of a function.

        Yes, like in Rust, for example. But it also has defined traits and other features on top of the chaining to deal with the same problems Go would suffer from it had such syntax. Theoretically Go could introduce the same, but it remains unclear how to do that in a way that makes sense in the Go language.

        Again, there is probably a solution out there, but nobody has come up with it yet. Surprisingly, these kind of things aren't sent down from the heavens by a magical deity. It takes human effort, which isn't there because they are busy ranting on HN.

        > It seems like "return err" is very useful for this pattern

        Where would you find it useful (memes aside)?

  • Cthulhu_ 9 days ago

    There were many proposals but none of them were an actual improvement over the simplicity and straightforwardness of the existing. `if (err != nil) {` is simple, short and to the point, and adding language features for only this use case wasn't deemed worth the cost in the end.

    • consteval 9 days ago

      The problem with this syntax is that it's not required anywhere, any time. It also makes the logic extraordinarily complex for what it is. You can very quickly get into branch hell. I hate to say this, but often the control flow is much simpler and easier to understand with exceptions. The "if" works fine for one level, but any deeper than that and it's no fun.

  • divan 9 days ago

    As with real sugar, we humans don’t have sensors that would tell us when there’s "too much sugar".

Arainach 9 days ago

I'm curious about your objection to the proposal. Sure, generics mean that libraries need a bit more syntax - that's true in all languages - but the actual consumption of the AssociationList type here is clean and readable.

Most types don't need to be generics. Containers do, and I prefer a bit of generics syntax to copy/pasting the container ten times for ten types.

  • JyB 9 days ago

    You spend more time reading code that writing it. Optimising for the later is a mistake. I guess the noticeable pushback against including generics was not unwarranted, people are just now starting to see the ripple effects we were warned about.

    • consteval 9 days ago

      Generics are, IMO, necessary for even a semi-modern language. Okay, you don't need a turing complete templating sublanguage like C++, but you do need at least a way to take generic functions and create generic containers.

      In application code you will almost never write generics. To me, it's always been a non-issue.

    • Arainach 9 days ago

      It's optimized so that it's easy to read the code that you read all the time: code iterating through the containers.

      It is dramatically less common to read through the implementation of containers.

imiric 9 days ago

Indeed. I still try to avoid generics whenever possible, and prefer a solution that doesn't use them. Thankfully, there aren't many scenarios where they're absolutely indispensable.

  • kubb 9 days ago

    Write a generic instantiator that scans your codebase for generic usage, and makes one copy of a generic for every type it's used with. Then you can remove all the generics and go back to copy and paste.

zapnuk 9 days ago

Writing custom iterators always looked bad and overly complicated.

If it's not essentials I'd rather not allow code like this in my codebase and use some other solution that is more readable.