Only everybody who asks for generics knows that, and still wants them to go through and pay the price.
The "we'll only add them when there's a solution with no tradeoffs" is a red-herring, like "we'll add them on a day that doesn't end in -y".
>And I think most people will agree that Go has achieved a lot of success without them, so perhaps most use cases don't require generics after all.
In the same sense C has achieved quite a lot with buffer overflows, so what's the point in something like Rust?
And those people are outnumbered by people who are comfortable and productive with the current Go implementation.
Programming languages are not all placed on a single Bad<---->Good scale. There are many variables involved, and it makes sense to have local maxima like Java, Go, Clojure, C, Haskell, etc.
The only way to know that would be to be able to compare one implementation with and one without generics. But only one Go implementation exists. Perhaps there's some toy attempt by someone, but not anything that is equal in all other aspects to mainland Go but with Generics on top.
Plus, since most of the burden falls on the compiler writers and most of the benefits to end users, it just takes the compiler writers to be "comfortable with the current Go implementation" for this to continue to remain Generic-less.
1) tons of people have asked for Generics, few have asked for HKTs.
2) C++, C# and Java, programming languages with millions of users and extremely battle tested have Generics, no mainstream language at that level offers HKTs.
3) As a result of (2) a ton more people are familiar with Generics than HKTs, and so the latter are much less probable to be requested.
And of course, if they do add Generics, and people ask for HKTs, they should consider what they do about that then and there, not now.
No reason to never make a step towards somewhere just because you might (or will) be asked to also take a further one.
Except if they believe that they are already at some optimal point (which is my case I think).
All I'm saying is this doesn't seems like a simple equation to me. There are many possible solutions to the problem of building large, correct, maintainable, performant programs - and neither of the existing ones looks like a surefire winner given all the limitations.
https://golangnews.com/stories/1490-results-of-the-gopher-la...
Personally, I love most things about go, but its utter clumsiness at iteration and data transformation relegate it to a fairly limited number of use cases in my mind.
I do also really value the lack of change in the language though, it is important and rare that it is stable and not accreting features every year (compare with rust or c++).
In fact, because I used Clojure just before switching to Go, I found not having immutable variables harder to get into the habit of more than anything else. I actually first used Go because I needed to write something that manipulated bits in memory reliably, and couldn't be bothered going back to Java.
As a counter-example, I'm not sure this is true.
Java didn't have generics. Then they didn't have lambdas. Now they're even redoing the classpath! Have they gone nuts?
No, they've been evolving, and they've kept on adding features as it was opportune and useful to do so. If tomorrow's programming paradigms depend on HKTs and dependent types, then, yes, Go might want to add them. Today's programmers are just about starting to understand what a dependent type even is, so you're suggesting to include them in Go in order to ridicule the potential inclusion of generics. This is a fallacy, because generics and dependent types are years, decades, apart. And generics are very much part of how several generations of programmers have learned, and expect to be able to program.
If your language stops changing, your language is already dead. Yet another point of similarity between natural and programming languages.
As a aside, why not add HKTs straight with generics? The problem the Go implementors have with generics seem to be about run-time representation (boxing or unboxed), but no additional run-time issues arise from HKTs. The main issues of HKTs are that type inference becomes harder. Haskell's approach of eschewing type-inference at the kind level (because it's undeciable anyway) but giving unkinded type variables the kind * seems to be a run-away success.
Irrelevant. Go already has parametric functions like append, it just doesn't allow user defined ones. append isn't enough when a programmer needs to remembers 10+ tricks to make up for the lack of generics :
Citation needed. How do we know that the majority of Go users would prefer not to have generics? How do we know the number of non-Go users would be willing to use Go if it had generics is insignificant? Is there some large scale survey of Go users somewhere you can cite?
Of course they are. The people who are not comfortable with the current implementation aren't using Go.
It's a fallacy to only look at what the major users wants. It's similar to the “faster horses” fallacy.
Not true. What about those who write Go at work for instance?
Russ actually addressed this at length: https://news.ycombinator.com/item?id=9622417
But Generics are neither some new PL topic, nor have they not passed the "filter of practical experience" (millions use them in C++, Java and C# alone).
I'd rather they explicitly said: "It will take a total breakthrough in computer science regarding generics implementations for us to consider them for Go, and we wont ever care of paying the costs of any current, viable in practice for millions of programmers in other mainstream languages, approach".
Well I (somewhat) agree with that, but my take of it was Russ is basically saying existing approaches "don't work well with Go" and "recent ideas" are "not well understood" with the concern that "there's a princess in another castle after that one I am sure."
Or put it another way, per his emphasis on the "engineering" nature of Go, it is not an issue of tradeoffs, rather a concern that a runaway train of complexity will follow. Or if you will excuse my Mexican French, the problem is the fucken type system :)
A programming language design is the combination of a set of features. The hard part in designing a language is working out how all these features interact with each other.
For example, one feature of Go is that every type has a default value (0 for decimals, nil for pointers, empty string for strings, etc.). This property leads to the inability to include algebraic data types cleanly. To see this, consider this simple ADT (in Haskell syntax):
data Either a b = Left a | Right b
What's the default type here? It could be "Left(default-value-of-a)" or "Right(default-value-of-b)". You could introduce additional syntax to define the default value, but that increases complexity. You could introduce a rule how the default value is inferred in these cases, but that goes against the Rule of Least Surprise. You could remove the "default value for every type" feature that clashes with ADTs, but that causes problems elsewhere. Therefore Golang lacks ADTs and uses pointers and multiple return values instead.This is a dangerous and slippery slope to engage on.
Java was also extremely successful in 2004, before it supported generics. Yet you'd be hard pressed to find a Java developer who thinks that generics were not overwhelmingly beneficial to the language, and probably one of the main reasons why Java is even more dominant and powerful today than it was pre-generics.
I am not saying generic are not useful. But I have not found enormous use of Generics in typical Java business application. The most common generic data structure I have used in Java are Maps and lists which are already generic in Go.
Thanks to generics, millions of lines of code in libraries that you are using became safer and faster, and you are benefiting from this, even if it's not apparent to you.
Basically, generics made Java a better and safer language. Both theoretically and practically.
It's hard to argue that a feature that appeared a decade later changed the equation all that much. I like generics but would still use Java even if they did not exist. In my work the concurrency features are far more important.
Designers of Go have been disciplined enough to keep things simple - let's you keep more of the problem domain you're trying to solve, along with the code to solve it, in your head. Try that with a big project using another toolchain - lots of frameworks, lots of places to plug your code, with less coherency (in my experience) across the various experience levels of your team. And I'm talking about big teams here, not 10 devs.
Subjective Success metric with comparable teams: Go vs <x with generics>. If Go team gets solution done faster, correct, within budget compared to other team, your stakeholders will not give a crap about what devs think about generics. There's a good chance that would happen, based on my experience.
Java's current popularity is due to Android, not the bloated corpse of enterprise Java.
Android certainly made it more popular than it already was, but Java was extremely popular way before Android appeared.
If Java had generics from day one, they could have put methods like equals and hashCode in interfaces that some classes don't implement, instead of passing static type checks and then blowing up at runtime because you did something that never made sense.
Internally, the Go team has drawn up proposals for adding generics [1] but rejected them because of drawbacks. Hopefully one day we can see those drawbacks, because maybe a lot of us are more than happy to pay that price.
[1] https://www.reddit.com/r/golang/comments/46bd5h/ama_we_are_t...
Hopefully one day we can see those drawbacks
One of the main issues with Go is that we can't see those discussions. With C++, C, Rust, D, Java those proposals are public from Start to Finish.From the discussions I've seen on what Go considers complicated I'd bet it work out to be something like name mangling.
Go turns off ASLR for loaded C code because its easier to debug. Which is really only true if your debugging process requires memorizing the absolute address of symbols that control flow will jump too. I guess that is useful if you are reading the raw hex streams.
Name Mangling is complicating things because you can no-longer debug with ElfRead.
Go seems to reject anything that was invented after the ~70's. As those concepts are complicated.
I can't help but feel that Thompson and Pike just don't want to change the bad habits they've developed from by-gone ages and adopt modern tooling.
Why wait for "one day"? These proposals are public: https://github.com/golang/proposal/blob/master/design/15292-...
They should have thought about it at the very beginning of Go design. it's easy to say now "We didn't find a good way to implement generics" when their design choices made implementing generics insanely hard at first place.
Well actually,they thought about it, where does append,delete,make and co come from? how do these functions know their arguments and return types at compile time? generics.
Furthermore it's not all or nothing. Go could have supported parametric functions in user land just like append,make or delete without implementing full-on generics. They just don't want to bother with that, because it's impossible to retrofit generics now without breaking the reflect package. The rest is just excuses to evade the issue.
> Generics are not free
Creating a modern statically typed language WITHOUT generics isn't free either. Just like implicit interfaces are not free, just like the reflect package is not free, just like using interface{} somewhere isn't free, just like telling people to use code generators isn't free.
That is, a []Whatever is not as opaque as a Whatever{} or an interface{}. Same with a map[Foo]Bar, you (you being Go) know it's a map.
(I have no opinion on whether Go should have generics, I just think the apparent exceptions make sense even if they might seem unfair.)
https://github.com/golang/proposal/blob/master/design/15292-...
Go doesn't have generics because they don't want them.
The truth is that one does not need generics. I've worked for decades in languages that don't have generics. The real question is with current software capabilities and requirements, is the convenience provided by adding generics to Go worth the cost of the added complexity. People clearly disagree, and the language designers decided "no". That's not dogma; that's engineering.
Sure ,when you can use interface {} AKA void pointers everywhere, you certainly don't need generics /s
You need generics if you want to avoid context.Context.Value(interface {})interface {} like API. You need generics if your goal is type safety AT COMPILE TIME. Obviously it's not one of the goals of Go /s ...
https://groups.google.com/forum/#!msg/golang-nuts/Rj5t1h_ztx...
Sure, the final implementation still has a couple instances of interface{}, but they are much more innocuous than your example.
The net/context package was designed to pass request lifetime context throughout your service infrastructure (think microservices), where the values provide metadata from your transport protocol. HTTP headers, if we want to think in terms of HTTP, although obviously there is no specific protocol dependency at this layer. The value may not even be assigned by a Go program, so it seems that runtime validation would be a necessity even with generics. A broken or malicious program upstream could easily inject something into the context that you weren't expecting, or simply not supply the value at all.
So, if you want to use types to make Context safer to use - and I'm not saying this is necessarily desirable, but assuming it is - you could start with something that works in the existing Go language: explicitly marking what kinds of objects can be keys rather than using interface{}.
type Key interface { _dummy_key_method() }
type Context interface {
...
func Value(key Key) interface{}
}
func WithValue(parent Context, key Key, val interface{}) Context
(Incidentally, I find it odd that WithValue is a freestanding function while Value is a method; I suppose this is to reduce the burden of reimplementing the Context interface for a new type, but is there any actual reason to want to do that?)This helps prevent you from accidentally using the wrong object as a key, a bug that might be tricky to catch if the key you meant to use is legitimately sometimes not present in the Context (in which case your code wouldn't error out due to nonpresence of the wrong key). It also prevents hacks like using strings as keys in lieu of declaring dedicated constants, which would create the risk of silent failure due to typos. (It seems this is already considered a bad practice, but such a signature would enforce it.)
Once you do that, it's apparent how generics could further improve safety. Using imaginary Go syntax:
type Key<T> interface { _dummy_key_method() }
type Context interface {
...
Value<T>(key Key<T>) *T
}
func WithValue<T>(parent Context, key Key<T>, val *T) Context
Now you're safe from type mismatches. Actually, it seems that best practice is already for packages to provide type safety by defining wrapper functions, such as this from userip: func NewContext(ctx context.Context, userIP net.IP) context.Context
func FromContext(ctx context.Context) (net.IP, bool)
That works well enough, but generics save you the boilerplate; packages could just expose the keys as public constants while retaining type safety. Admittedly, those two functions are only a few lines to define, so there's not that much boilerplate to save, but it's something. Generics would also make it possible to write type-safe generic helper functions involving contexts, though tbh I can't think of many obvious ones you'd want. Maybe a function that panics if it can't find the key: func MustHaveValue<T>(ctx Context, key Key<T>) T
...though that would be better handled by a proper Option<T> type anyway. But that too would be enabled by generics!Okay, but there's still more that the compiler could guarantee. The value is now guaranteed to have the right type if it's there, but for all we know it could be missing. Indeed, this is probably a bigger footgun than types are in practice: it's easy to accidentally have some code path that bypasses the code responsible for setting a particular key. Unfortunately, this is a harder problem to solve even with generics, and in fact multiple Rust web frameworks have run into very similar issues:
https://github.com/nickel-org/nickel.rs/issues/79
https://www.reddit.com/r/rust/comments/2cl7bw/web_frameworks...
Arguably what you really want here is extensible records with a structural type system. For example, in Elm, you can have the expression
{ foo = "1", bar = "2" }
and it'll be of type { foo : String, bar : String }
But then, given any record type, you can add more stuff onto it (I think this works but haven't tested it): type alias HasFoo a = { a | foo : String }
addFoo : a -> HasFoo a
addFoo rec = { rec | foo = "1" }
With a feature like that, your functions' context parameters, instead of having the plain Context type, could explicitly specify that they need a Context with a given key. Thanks to generics, you could call functions that add new keys without losing track of the keys already in the map; for example, with the above Elm code, if you called addFoo { bar = "2" }
the result type would be { bar : String, foo : String }
In fact, it is possible to simulate this in languages that lack native support for extensible records but do have sophisticated enough type systems - anything from C++ to Rust to Haskell. I think the Iron web framework for Rust does that now, though I only took a quick glance at the docs. I'm not sure exactly what this would look like in Go, both because the implementations tend to get pretty ugly/hacky, and because a hypothetical generics feature for Go might not add enough power to make this expressible. (Which, to be clear, isn't necessarily a bad thing; every bit of complexity has a price, and C++ templates in particular are notoriously awful to read and debug.)The fact that interface code often has runtime failures is usually due to a feature Go interfaces get you that many languages with generics don't have in the first place -- the ability to downcast to concrete types.
Now, interfaces sadly do not have the same amount of type safety at compile time for defining new containers and you can't do things like return value polymorphism as well, but it's not that bad.
It requires a different style of programming (the post from rsc linked in the sibling comment is particularly good at illustrating this), but it's not that bad.
I say this as a Rust programmer (also a Go programmer, but primarily a Rust programmer) who absolutely loves generics.
you're assuming you know the kind of problems I'm trying to solve everyday. You don't. Don't patronize me.
I write a lot of algorithms that can work with float, double, complex float, and complex double. Without generics/templates (or a macro pre-processor), I have to cut and paste that code 4 times, or I have to sacrifice performance. Maybe I don't need templates, but I really really want them.
The general consensus from the Go developers is probably best summarized as "Generics are useful, but each of the implementation methods we have seen so far have significant tradeoffs, and the benefits are outweighed by the costs of the methods we've seen."
Which isn't to say that Go will never see generics, if someone finds the right way to implement them.
I think what people tend to disagree with is the last part. There is a lot of leeway in estimating the costs and benefits and people end up with very different estimates.
Still, if you really feel passionately that Go with generics would be frickin' awesome, generics seem like a pretty easy thing to write a preprocessor for. With the great knowledge of programming language design and implementation that most pro-go-generics internet forum posters have, it should be a piece of cake.
I am basically doing this: https://github.com/lukechampine/ply
Ply is like Go except you can write things like []int{1,2,3}.filter(even) and it will Just Werk.
It's not quite ready yet, but it feels promising. The idea is that Go doesn't really need full support for generics; most people just want a few more higher-order functions available for manipulating slices, maps, and channels.
It's how C++ started. Unfortunately, that's how it will also end for Go, because of Go designers ignoring the past.
That said, I'm curious to know if Have is able to eliminate intermediate structures (deforesting) when chaining together transformations. Haskell performs "stream fusion" to accomplish this, but I'm not aware of how it's done in other languages. Ply does support this optimization, and that's mostly possible because it restricts the generics to a built-in set (rather than allowing the programmer to define their own generic functions).
My other goal with Ply is to make it as familiar as possible to Go programmers. It's not so much inventing a new language as it is scratching an itch.
Your second sentence answers your first. Not everyone likes writing the same code over and over again, even if it's simple code.
> generics seem like a pretty easy thing to write a preprocessor for
Very funny. But even if it were possible, the bigger problem with using a preprocessor is that everyone ends up using their own incompatible generics preprocessor.
Others have pointed out this is how we got C++ and stopped putting method dispatch and stack unwinding boilerplate at the same level as the code we really care about.
It definitely works out. There's just a question of whether it's an end result that we want.
There were many motivations for C++ -- and many more seemed to get added as it went along -- it's not just generics parametric polymorphism. Of course I would hate to say: Go should have every language feature or else we'll end up with Go++ or Objective-Go or something like that. But simplicity is not just a matter of not providing for something.
https://research.swtch.com/generic https://news.ycombinator.com/item?id=9622417
I don't have a good feel for whether that's true of the Go community, or maybe more importantly whether it's relatively more true of the Go community than, e.g., the Python community.
I too am rather surprised to see Go, Lisp and Haskell cited as languages designed by people who are not dogmatic. This may well be true at the micro-level of decisions like "how do you best compile green threads", but at the higher design levels all three languages are very, very opinionated.
I think statement in particular is questionable:
Contrast that kind of discussion with the heated arguments or overly zealous statements you sometimes see from users of the same languages. There's a real disconnect, possibly because the users don't have the experience of weighing the arguments on both sides and don't realize how easily a particular decision might have gone the other way.
What is this implying? The designers of Go and Haskell never make "overly zealous statements" or "heated arguments"? Isn't the history of Go littered with such arguments, the most famous being that Go is simple because it's designed for Google engineers, who can't handle complex languages?
It's definitely true that language communities are often zealous: when library interop is bad they have to be, in order to get an ecosystem of libraries and tools. That's why you see people posting things like "I made a terminal emulator in Rust" to Hacker News but don't see people posting the same for C++ or Java. Those languages have established ecosystems already and need much less evangelism.
Haskell developers have a clear position of letting features get in the language as the community pushes for, and the overall guidance is very much open to changes. The one thing you won't hear there is any equivalent of "generics are bad for you, we are not adding them".
Monads I can grasp, but never got how they are like donuts.
http://dtrace.org/blogs/wesolows/2014/12/29/golang-is-trash/
I think he's just trying to say that people should be less tribal and dogmatic about their tools, ,and users should try to understand that language design is about trade offs, not holy war. I think it is unhelpful to lump rsc in with rob pike, they are very different personalities.
There were a lot of discussions on generics, and the language authors and contributors were certainly open to ideas and indeed did their own fair share of research. For example, see https://research.swtch.com/generic from 2009. Indeed, the FAQ entry itself on Generics states 'Generics may well be added at some point. We don't feel an urgency for them, although we understand some programmers do.', and looking at the blame log, that's from 2010 or earlier, and it's stuck to this day.
So, from everything I can see, the creators themselves are certainly open to the concept - I don't see any "you don't need generics" coming from that side.
https://github.com/golang/proposal/blob/master/design/15292-...
Their argument has never been that you don't need generics. It has been we don't want to add them in the currently available forms right now and here are the ways you can get around their absence in the meantime.
Just to be clear, I personally dislike tabs and line-lengths over 80 characters. However, such choices have no bearing on the success or failure of any project I have ever experienced.
Cross-project style consistency doesn't buy us anything reasonable--there's no "engineering" about it.
Also, braces around conditional logic isn't what I'm talking about here (since that's not a "go fmt" style choice--that's the language syntax).
Most project teams have limited resources to come up with a solution that is close enough to correct to make the project pay off. In new technology there also tends to be a small set of technical issues that are hard and that you must get right--nothing else matters that much if you fail on the core. I am pretty heartless about anticipating things like code conventions, security requirements, legal review, etc. and just following the rules or otherwise making them non-issues for the project team in areas where it does not fundamentally alter the outcome.
Edit: clarification