It seems to me like generics are extremely important for "library code", and not super important for "application code" (and in fact they can sometimes create more confusion than they're worth in the latter context). Go also seems like a language that thrives in smaller-scale, application-focused contexts (microservices being the obvious example).
So in this light, and with the basic generic data structures supplied by the standard library, it seems to make sense for "user-level" Go code to generally be better-off without generics
Of course the line between "library" and "application" code isn't well-defined (especially if you consider libraries outside of the standard one), which is probably where most of the pain-points are coming in
> It seems to me like generics are extremely important for "library code", and not super important for "application code"
I find that it really depends a lot on the language you're working in, and how well it does generics.
In Java, I don't use generics much beyond collections, streams, stuff like that. Whenever I try, I tend to trip over its relatively limited implementation of the concept.
In a language like F#, on the other hand, generics are the cornerstone of my business domain modeling. They provide a way to map everything out in a way that is much more concise, readable, type-safe, and maintainable than I find to be possible in many other languages.
I have yet to kick higher-kinded polymorphism's tires in a good context, but I can see where a good implementation of it would move things even further in that direction.
(edit: Disclaimer: This isn't meant to be a statement on Go or the advisability of this proposal. Go isn't really meant for the kinds of applications where I've seen real benefit from generics.)
Whether you find yourself using them and whether they're actually necessary are two different things :)
I've gotten use out of generics in "application code", but I've also been bewildered by overly-complex generics-within-generics-within-generics written by other people in application code. It's hard to be conclusive, but I wouldn't be surprised if they've done more harm than good across application contexts.
To me, that's a shining example of the problems I've run myself into when trying to squeeze much power out of Java-style generics. I never seem to encounter similar problems in F#. Scala, it depends on how successful I am at not losing a boot in the mud.
Generic programming was born in a language whose other pioneering features were algebraic data types and an HM type system. I've never really seen a first-rate example of one that didn't come paired with at least passable examples of the others.
It's a real pain in the ass not having generics any time you're working with algorithms and data structures. Linked lists? Graphs? Trees? Go is generally quite nice to work with but it implementing these basic structures again and again with different underlying data types makes me feel like I'm writing Java. Which is ironic because, you know, Java has generics.
I think the idea is that these fundamentals could/should be supplied by the standard library
Ironically, despite all their differences, Rust actually has a similar situation: it's really hard to write the fundamental data-structures in Rust, so they've put a focus on having really good standard-library implementations and people are generally content using those (in Rust's case it's because the borrow-checker makes pointer twiddling hard, but the outcome is similar)
They kind of did this with maps and slices except that they baked them right into the language instead of the standard library. Like, map is a keyword. The standard library doesn't have many data structures at all because, well, without generics they're not very useful. There's a few things like a linked list and a thread-safe map that accept interface{} types but then you're basically throwing the type system out the window.
> I think the idea is that these fundamentals could/should be supplied by the standard library
There is basically no limit to the number of data structures possible, nor to the possible implementation details of most of them, all of which can be relevant to the situation at hand.
The stdlib can hardly be expected to implement them all.
If they're very specific to the situation at hand, they're much less likely to need to be generic. The GP explicitly mentioned "Linked Lists" and "Trees". You don't need to be writing your own linked-list or (basic) tree from scratch.
> The GP explicitly mentioned "Linked Lists" and "Trees".
And graphs.
> You don't need to be writing your own linked-list or (basic) tree from scratch.
Trees are rarely useful in and of themselves, what's useful is the data structures you're building out of them. And that, in turns, informs a significant number of properties of the tree you'll be using as well as the operations to perform. The stdlib providing "a basic tree" and essentially telling users to get bent would be worse than useless, it would be actively insulting.
Even for the humble "linked list" there are half a dozen possibilities: singly linked? Immutable? Doubly-linked? Circular? Array-of-nodes?
> I think the idea is that these fundamentals could/should be supplied by the standard library
Data structures generally need to be parameterized on the contained types of you don't want to waste the effort of even having a static type system, which makes it impossible to do this right without generics
Though again, Go as a whole seems ill-suited for scaling to larger projects because of lots of other limitations on its type system, reliance on conventions, implicit-defaults, etc. Which makes it well-suited to (and often used for) things like microservices, where each actual codebase is smallish. Codebases like these will tend towards having less "library-like" code anyway, which means they don't need generics as badly. There's synergy here in the language design.
So I guess what I'm saying is: leaving out generics seems like the more "Go-like" direction, will dovetail better with its overall philosophy, etc, and isn't without advantages. But it would also mean kneecapping the language when it comes to certain use-cases that it's never going to be great for anyway. It's the classic "opinionated" vs "everything for everybody" dichotomy
Why do you say Go has trouble scaling to large code bases? Is that something you'd expect, or something borne out by the evidence? And if so, what is the evidence?
FWIW I would take fasterthanli.me with a grain of salt. The guy is a serial Go hater. His points stand on their own, but I don't think he appreciates Go's benefits. I think "A Philosophy of Software Design," Rob Pike's talks, or Russ Cox's blog posts are a good place to look if you want to understand what is valuable about Go and the reason to believe it would actually scale very well to large codebases.
Thanks for the references, I'll look at what the other side has to say
I am aware that fasterthanli.me can be a bit, shall we say... opinionated. Though as you say, his points do stand on their own. I can see the things he points out about the design philosophy of Go's language features and standard library and draw parallels to languages and libraries that I've used firsthand, and had firsthand frustrating experiences with when it came to navigating their magical behavior and lack of enforcement of contracts. But I'll keep an open-mind
Certainly. As a counter-example to the pain of implicit functionality, take the UNIX file API. `open(1), write(1), close(2)...` represent hundreds of thousands of lines of code, spanning network devices, local file storage, integrity checking, and who knows what else, and all of it is hidden. It is precisely the mountainous heap of implicit behavior that gives these APIs value.
That being said, APIs with implicit function that are broken or surprising are painful, but I take this not as an indictment of implicit function, but as an indictment of buggy APIs. I think state and hidden functionality is the essential ingredient of highly useful code.
So in this light, and with the basic generic data structures supplied by the standard library, it seems to make sense for "user-level" Go code to generally be better-off without generics
Of course the line between "library" and "application" code isn't well-defined (especially if you consider libraries outside of the standard one), which is probably where most of the pain-points are coming in