Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Honestly, I’ve written some applications that, on paper, should be the perfect candidate for generics. And yet I can still count on one hand the number of times generics have saved me from massive code duplication.

Most of the time generics might be useful, I’ve ended up needing reflection too anyway. And at that point, I’m really no better off for generics.



I understand that this is true for a lot of application code. It's not true for library authors though, and every language needs libraries.


I’ve written a lot of libraries too.

The problem is generics only solve a very small part of the equation: compile time checks for composite types. But to use composite types in anything non-trivial in Go, you then need reflection. Which is slow. And if you then need reflection, you’re already passing interface types anyway plus you’re back to having to handle type-handling errors in the runtime.

So if you’re writing a library that’s expected to have any kind of performance, you’re back to code duplication and having a DoSomethingType() function signatures again.

Or you stick with reflection and take that performance hit PLUS the risk of compile time constraints being runtime errors; which is the a lose-lose scenario. And let’s also not forget that reflection can be just as verbose as code duplication, and harder to get right too.

Don’t get me wrong, I’m glad we have generics. But people on HN massively overstate the value of them in a AOT non-dynamic, strictly typed language like Go.

I guess you could argue that Go has other shortcomings that directly result in generics having limited value. But then you’re basically just arguing that you prefer coding in a different language paradigm, and at that point, you’re much better off using that other paradigm instead of complaining that Go isn’t JavaScript or Haskell.


Yes it is precisely Go’s shortcomings that would require typical use of generics to also require reflection most of the time. You would want Rust traits (or Haskell type classes) or C++ style type traits and then the need for reflection is much reduced. So Go has painted itself into a corner where generics feel bolted on and less useful than generics in other languages. It’s still Go’s fault and people rightfully argue that they should prefer a different language.

The Go language itself is never its strength but it has a good runtime, wonderful standard library and tooling. People never picked Go for being an amazing language, but rather for these other things.


I picked Go for the language, so your argument is incorrect.

Go’s shortcoming is also its strength. Language design is a constant battle of tradeoffs. And I happen to find many of the decisions C++ and Rust made weren’t analogous with my preferences.

> It’s still Go’s fault and people rightfully argue that they should prefer a different language.

It’s no more Gos fault than it is the people using Go. It’s called an “opinion” and “preference”. Please don’t assuming your preference is some global truth, because it is not.


>You would want Rust traits

Go generics does have this feature. You can require a generic type to implement an interface.


Requiring the implementation of a Go interface is less powerful than requiring a trait.

It reminded me of early years of Rust where people saw traits and asked, well Java had interfaces for many decades, what’s the innovation?


I'm not sure you can really argue that Rust's traits are innovative. Haskell typeclasses, OO-style abstract interfaces, and C++-style associated types were already well-known and well-established features. The particular mix of those features in Rust may be new, but the same could be said for Go's, or really any programming language's, particular mix of features.

You're right of course that traits are more powerful than interfaces. Go simply isn't designed to satisfy fans of elaborate type systems. This can be frustrating at times, but it also means that you don't have to deal with overarchitected library code like this: https://github.com/marshallpierce/rust-base64/issues/213 (Yes, you can overarchitect code in Go too, but the simplicity of the language, and the model of the stdlib, do tend to discourage this in practice.)

I've never written Rust professionally, but I did have a Haskell job for a couple of years. IME the sophistication of the type system is a double-edged sword. Sometimes it lets you concisely express important invariants; sometimes you end up spending way too much time writing elaborate types for code that isn't really doing anything very interesting. I understand the appeal of fancy type systems. In practice, I get a productivity boost from any kind of basic static typing, and then rapidly diminishing returns from the fancier stuff.


Interesting, thanks - is the problem you’re describing solved by Rust’s macros (eg derive) or are there further issues you see even there?


Thanks for that, nicely put, interesting angle.


Generics are supremely useful for containers. They are kind of one trick ponies in that sense in application code. Similar to reflection, I’d say, which is utilized for serialization 99% of the time.

The thing is that one use case is so essential, so foundational, that we really can’t just skip it. You need generic containers, for ergonomics and performance. I mean, compare C qsort to C++ std::sort.

The languages that “get around” generics, like PHP, include god containers in the runtime. The language I’m designing is also that way, I’d like to avoid generics preferably forever.

But there’s a tradeoff there. God containers are very flexible, and we’ve seen the ramifications of untyped PHP arrays.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: