That said, there are still cases where generics would be useful; however, a lot of the people who complain about generics missing from Go tend to exaggerate the extent to which this is a problem, "You can't ship software in a language without generics!". In the case of Go, you have something like 97% type safe code compared to the many, many successful Python and JavaScript programs that ship with 0% type safety. And lastly, one thing that you can't really appreciate without trying Go--the lack of generics/expressiveness and general simplicity of the language largely means everyone writes the same boring, predictable code, which means you can pretty much pick up any project on GitHub or your own code from years ago and understand it almost immediately. This isn't to say that generics aren't worth it; only that the debate kind of sucks because one side is ignoring that rather significant point.
Isn't this is a strawman? Most arguments in favor of generics in this thread and elsewhere are well informed and people just want safer code...
As you said, you can already have generics using unsafe solutions. Some built-in types have generics. Most people just want this type safety.
interface{} does feel like a temporary and clunky solution for people used to generics.
> And lastly, one thing that you can't really appreciate without trying Go--the lack of generics/expressiveness and general simplicity of the language largely means everyone writes the same boring, predictable code, which means you can pretty much pick up any project on GitHub or your own code from years ago and understand it almost immediately.
I don't get this argument. Why are generics more complicated than other features? Has this hypothesis been tested?
I personally find that using generics is way easier and faster than interface{}, Reflection or code generation. Also simpler and less error-prone than features from other languages like inheritance, operator overloading and unbounded coroutines.
As a C programmer (which doesn't have generics either) I can't recall the last time where that additional safety was being missed. Whenever I fed the wrong argument in for a void* parameter (which is rare enough), the program crashed on the first try and the problem is obvious. Plus, Go even has runtime type checking to catch this sort of problem with 100% probability at runtime (I think - I'm not a Go programmer).
So I don't think there's a valid problem here.
Maybe the more serious problem is that people want to be able to quickly say "I want a Fibonacci-Tree<K,V> for types K and V"?
Except when it isn't. Except when it crashes in production. I believe this is the kind of situation people are trying to avoid with type safety...
> Maybe the more serious problem is that people want to be able to quickly say "I want a Fibonacci-Tree<K,V> for types K and V"?
That's uncalled for, isn't it? There's a lot of non-toy problems solved elegantly and pragmatically using generics. I personally enjoy how Entity Framework uses it.
Yes, but by far more important are bounds checks. Go has bounds checks. As a C programmer I don't get to enjoy them (I get to write simpler, non-GCed, object-cruft-free software in exchange). Out of bounds reads and writes consume far far more of my time compared to void pointers. (And even OOB are not that time consuming).
> That's uncalled for, isn't it? There's a lot of non-toy problems...
Yes, the Fibonacci was a bad example (couldn't use Map because Go has that). It wasn't meant in a mean way.
Sure, but I'm failing to see why having other checks isn't important. I mean, generics aren't hard to use... They're way easier to grasp than Channels (a great feature), and easier to use daily than go generate. I dunno.
Generics are abstract. They lead to bad error messages. They make the language more complex. They require syntactical support as well as special magic under the hood. Look for various resources by Russ Cox and Rob Pike (and maybe other Go designers). For example https://go.googlesource.com/proposal/+/master/design/go2draf...
About your other points: nope, generics wouldn't remove simplicity in the language itself. They would mostly legitimize with type-safety certain idioms that are already present in current Golang code. And the terrible error messages of C++ templates are not really a good example. There are much better languages now when it comes to generics.
I suggest that maybe you try looking into other legitimate uses of generics? The Fibonacci-Tree<K,V> example you gave earlier is unrealistic, and the other example being thrown around here, STL, is too complex... There are lots of legitimate reasons for someone wanting generics in a language, and nobody is advocating for them out of bad faith, or wishing to kill a nice language with feature bloat :(
For the rest... Yes, it is very ugly. But the standard generic does not fit very well with go type system, so....
Luckily, go is very simple. Even ugly bit are not hard to understand.
Why not? Generics in Arrays/Maps/Slices fit perfectly in the language, and don't feel weird at all in Go.
They are widely used data structures and so, in the opinion of the Go creators, justified separate specialized implementations in the Go compiler. In other words, there isn't actually a Generics system in the language (I don't actually know this, correct me if I'm wrong). For various reasons. There are quite a few talks and design docs from Rob Pike and Russ Cox if you look for them.
And generics aren't just for abstract data structures, there are other uses as well.
func (p *parser) tryIdentOrType() ast.Expr {
....
case token.MAP:
return p.parseMapType()
There is a special-cased function parseMapType() function in the Go parser. This isn't generic at all. I mean even the syntax (as in func foo(bar map[String]int) ...) does not seem to be generic at all.Yes, you can use any type as keys and values. But that's a far cry from an abstract system that lets the user define any parameterizable data type. And that's for good reason: By hardcoding just the most important parameterizable data types, the problems that an abstract system would bring can be avoided.
It's not a strawman; I run into this tired argument too often, so I wanted to call it out to avoid the predictable digression. There is a strong case for generics and I empathize with "`interface{}` is clunky..."; I just want a debate that recognizes the tradeoffs.
> I don't get this argument. Why are generics more complicated than other features? Has this hypothesis been tested?
I don't think I made the argument that "generics are more complicated than other features". My argument was that Go code is boring and predictable (consistent). It stands to reason that boring, consistent code is easier to read and understand than novel and/or mixed-paradigm code. Generics increase expressiveness, which pretty much by definition means fewer rails to keep code consistent. I'm not arguing that Go's current feature set strikes an optimal balance between expressiveness and consistency for all problems; however, it does do a pretty good job and we should count the costs as well as the gains when considering a new feature.
I agree that C++ template meta programming might be a bit too much for Go (or for any language, for that matter), but generics as they are implemented in C# and Java are pretty cool and simple.
IMO, if generics were to be ever added in Go they should just be a simple replacement for some uses of interface{} and code generation. I mean, the complexity is already there... why wouldn't generics simplify those use cases?
For example, in Go, there is no functional-vs-imperative conundrum; it's only imperative. And I appreciate that there are multi-paradigm languages like C# and Java that allow for both (better for experimenting with new patterns and paradigms); however, I also appreciate that there are languages like Go that take a more conservative opinion, and I think this is more practical for software development.
But why? There's nothing in generics that would make Go non-consistent. In fact, there's already generics in Go in Array/Map/Slice, and they're not inconsistent with the rest of the language at all.
> For example, in Go, there is no functional-vs-imperative conundrum; it's only imperative
Why would generics break that? Funcional programming and type-safety/polymorphism are orthogonal concepts. IIRC, generics were introduced in Barbara Liskov's CLU language, which is imperative.
It sounds to me that most people advocating generics in Go are having a pragmatic/utilitarian approach, while people against it are opposing it from a purely ideological standpoint not grounded in either theory or pragmatism.
And it sounds to me like you're working rather hard to misunderstand me/my-opinion and paint me an idealogue. :)
Even if I were making a positive assertion like "generics costs more than it gains", that would be a pragmatic viewpoint even if it's incorrect. But I'm not even making that assertion, I'm merely unsure and would like to be persuaded otherwise (and I find people with experience on both sides of the fence to be more credible than those without that experience).
> Why would generics break that?
Because "generics" as a feature suffices to support multi-paradigm programming. It explodes the solution space, creating many solutions for problems that Go's type system is perfectly capable of dealing with (~97% of the problem space in practice according to my experience) while permitting new solutions that Go's type system must currently punt on (~3%). We should be able to roughly agree on this even if we disagree about the degree to which multiple solutions is a problem relative to the advantage of the additional type safety.
That's fair! I'm really sorry I typed that, it was completely uncalled for and I wasn't referring to you specifically!
> Because "generics" as a feature suffices to support multi-paradigm programming.
I don't really agree with that. Despite being widely used in functional languages, they aren't really a "functional" thing, as much as static typing is.
In fact, I'd argue that Go already has a feature that is much more important and representative of funcional programming than anything else: higher order functions. [1] With higher order functions (and recursion!) you can implement pretty much anything functional.
Generics don't really allow for much more than something like interface{} can already give. The problem is that interface{} comes with both runtime performance and type-safety penalties. Generics could fix that and help programmers arrive at better/safer practices, IMO.
My point is: with generics the language could be much simpler and we'd have to rely less on (IMO) complicated/unsafe features like Reflection and interface{}.
[1] http://aquaraga.github.io/functional-programming/golang/2016...
Yes, there's a class of programs for which this is not the case, and for which I might want some sort of fancier guarantees. But those don't tend to be the sort of programs I write. My hobby projects are in Common Lisp; I used to professionally program in Python and JavaScript (for my sins) — Go is strictly better than those in the static-typing department.
Would I like generics? Sure, I can see how they'd be nice. But I remember how templates in C++ turned into something nasty, and I've written Java professionally too: I like how clean & simple Go is. I can write code, then be done with it, and come back a year or two later and not be mystified. It's a decent little language for getting stuff done.
But to answer your question go has really mediocre support for a wide variety of problems. Some that burn me a lot is support for future/promises and support for modern lock free algorithms.
So you end up either writing non-optimal replacements, losing type safety or in some case code generation is used.
This doesn't make sense in Go. Nothing is asynchronous in Go, everything is blocking. You use goroutines to execute multiple tasks at the same time. That's a big part of what makes Go simple: it's a lot easier to understand sequential code than spaghetti callbacks and promises (it's somewhat similar to async/await in the JavaScript world).
Go’s unsophisticated type system makes it difficult to write a good standard generic data structure library such as a future/promise library.
That leads to people either writing a poor replacement, not using that style, or losing type safety. This is precisely what you would predict upon learning golang doesn’t have user defined genetics & ive had it bite me more than once in real projects.
I think people are used to futures etc, that's why the new concept feels strange.
I think the goroutines and channels are the biggest features which compensates for generics.
Goroutinue are very light weight. Zero size channel works like promise
- container/list.List which uses its own Element with internal pointers and interface{} for values
- write a type-specific implementation (not generic, but no runtime type assertion)
- write your own implementation using your own node / element interface type (maybe your list is generic to types that support io.Writer - generic for some use, but if you need access to specific types, you're back to runtime type assertions)
- write your own interface{} implementation (constantly needs runtime type assertions)
- use code generation (maintenance is harder, potentially non-standard tooling / build steps, but hey - no runtime type assertions)
- use a slice instead of a list (Go's version of vectors / dynamic arrays) - still requires some implementation, but the language has a lot of built-ins that make this easier. Depends on use. If you are doing a lot of insertions in the middle, this may suck, FIFO may suck and you need to be careful about not doing append()-shift and effectively leaking memory, LIFO this is dynamic and gives you good cache locality. Up shot is no-runtime type stuff and at least the slices themselves and the built-ins are generic.
Whereas all the other kinds of generics, I believe, can't offer this quality. They require a lot of machine code instanciations (like C++'s template system). Or at least, in the case of virtual functions ("dynamic polymorphism" with VTables), they cause more brittle abstraction boundaries. Think how in C++, when definining a class, you need to expose a lot of internal details in the header file -- list all the private member functions and fields, etc. This has lead to the so-called PIMPL idiom which is exactly what you do in Go or C.
Even on C++ this is be eventually a thing of the past, after modules get finally adopted.
Using how C++ currently does as argument against generics is a pretty weak argument, given the various ways to implemente them since CLU and ML introduced generic programming into the world.
> Using how C++ currently does as argument against generics is a pretty weak argument, given the various ways to implemente them since CLU and ML introduced generic programming into the world.
I would be interested to know how you think the problem of tighter coupling (a.k.a dependencies) is solveable, because I don't see a solution. For example, I'm pretty sure that typeclasses in Haskell are implemented with either of those two approaches I've mentioned depending on the situation.
(Maybe it's possible if we require more information for the linker? I haven't thought through this.)
It is solvable by not sitting in an ivory tower using C++ and Java as the typical examples why Go won't get generics, and instead engage with generics friendly communities.
Personally I care about true, hard dependencies much more than that "exposed for everyone else to see" fluff (the latter being only a subset of the former). Probably that is because I don't do business software, don't work in big teams etc.
What really matters to me is reducing hard technical dependencies, and recompile times and ABI compatibility may only be the two biggest reasons.
You're probably aware that I program predominantly in C, and "sitting in an ivory tower" is not a word that I would apply to typical C programmers. I wouldn't apply it to Java programmers, either. It's normally a term for the academic world, the world from which languages that focus on type theory originate.
C does provide minimal support for generics since C11, with improvments planned for later standard revisions, as anyone that works predominantly in C should be aware.
And yes I am aware that I kind of reversed the meaning of the term, but that is how it feels all the Luddite arguments against generics.
Just because there are plenty of old tech languages, which many in the generics crowd have a large experience using, doesn't mean we should keep using such approach at expense of productivity.
They are actively looking for something that doesn't suck. Aren't they?
> C does provide minimal support for generics since C11, with improvments planned for later standard revisions, as anyone that works predominantly in C should be aware.
Not sure what was your intention behind that statement. But of course I'm aware of C11 Generics. And they might be nice for a generic min() and max() and similar but other than that honestly I have little use because types are not values and in C, types are not elevated to be much more than simply physical layout. Whenever I've tried to encode meaning in types in any language I've failed spectacularly. Feel free to make Oddintegers and Evenintegers all day long if that's what makes you happy.
> And yes I am aware that I kind of reversed the meaning of the term, but that is how it feels all the Luddite arguments against generics.
Let's get a life and not spend all of our time raging against people doing different things differently. At Google, I'm sure Go does solve real headaches they have with C++ ((re)compilation issues being only the worst offenders) and Python (performance...). And probably it does so better than any other language can. Because, Google is still primarily an engineering company, not a hipster company, nor a language-obsession company.
It looks more of "we are working on it excuse" to keep people at bay.
Rob Pike has done a presentation last year where he stated that he doesn't have anything to do with it and is very sceptical of it being ever added.
So WIP is kind of euphemism.
> Not sure what was your intention behind that statement.
That even languages born before generics are willing to move forward.
Google is majorly a Java, Python, C++ shop. Go is usually more outside than internal Google projects.
If we reduce Go lack of features to stuff that C doesn't provide, maybe bounds checking, modules and GC aren't required as well.
As for complaining, projects like Docker and K8s ensure that those of us that rather not use Go, have to deal with it in some form anyway.
Well that was the initial basic design frame: A systems programming language that removes some of the headaches of writing distributed systems software with the use of a GC and Channels/CSP.
It was intended from the start to have more features and tradeoffs than C has, and it even backed up pretty quickly from trying to be a competitor in the (not-distributed) systems programming space, didn't it?
> That even languages born before generics are willing to move forward.
C11 Generics are a pretty limited feature, basically a central switch on types instead of values. They are not a big deal. And maybe Go decided against them because Go has RTTI. In any case, C11 Generics are decidedly different from what many people expect from a Generics in Go.
> As for complaining, projects like Docker and K8s ensure that those of us that rather not use Go, have to deal with it in some form anyway.
Dude... 1) They are not Go. 2) If you don't like them don't deal with them. 3) Don't make an elephant from a mosquito. 4) Show us some of your beautiful systems software written in perfect OOP style in languages from 1975 already! We need real world proof that there is a supreme super-typesafe OOP way with functional bells that solves all of our problems!
Also some would say that Go is actually from 1968.
Generics specifically refer to the parameterization of types using types. Go doesn't have user defined generics.
The only reason in C++ why you need to define all the "internal details" in the header file is because the compiler needs to know the size of things. You can write completely template free code that requires all your classes to be defined in headers or you can litter your code with templates, put pointers everywhere and place all your classes outside of headers. It has nothing to do with generics and everything to do with how the rest of C++ is designed.
Other than that, I don't think we're in disagreement here.
If you really want high performance with many different types of a function, its a case of copy-paste-modifying your code.
Its a language that is straight-forward, its the antitheisis of tmtowtdi.
Golang use duck typing. It is very flexible and very simple. (Some times too simple)