I'm glad I'm not the only one sad to see generics invading a perfectly good language.
Programmers love generics because they enable higher-order abstractions, and programmers love abstractions -- code that isn't DRY is like an itch we need to scratch. Programmers also hate special cases, because they feel restrictive and inconsistent: I recall quite a few people protesting that Go's 'range' keyword shouldn't be restricted to a handful of built-in types, but rather should support any type implementing an Iterator interface.
But Go was not designed to be perfectly consistent or to enable high-order abstractions. It just aims to be productive at scale, and that often means restricting the programmer. Go tries to be non-magical, and sadly that can take much of the whimsy out of programming. You can't chain together operators in point-free style, or add a property to every 'Object', or directly increment a string, or anything like that. In other words, it's a terrible language for code golfing. But that sort of code has no place in a production environment anyway. It's "clever" code that gives the programmer a little dopamine hit when he writes it, and his co-workers a headache three months later. Those are misaligned incentives.
So I worry about generics because they let you abstract with wild abandon. They let you inject a little magic. And I fear that programmers will be seduced by their love of magic, and I'll be the one who ends up paying the price.
Writing the exact same code for float and double (and possibly complex float and/or complex double) sucks. You've created some straw man about dopamine junky programmers because you don't need generics for whatever it is you do. If you wrote numerical algorithms you'd be annoyed at any language which lacks generics.
Maybe that's the issue. From what I have seen, the lack of generics mainly becomes painful when working in the numerical space. If I work on mapping JSON, doing APIs and CLIs I don't really need generics. If I work on algorithms and data bags then it starts to be annoying re-implementing the same thing over and over again.
So in these discussion you have devops-type people mainly dealing with APIs that are perfectly content. And the ones that work with algorithms are talking past them because they don't experience the same pain.
Instead of changing the language to enable generics everywhere, maybe there is a lighter version that exists that only applies to numerical problems. Is it possible that a boxed "Number" type would solve most of the problems?
I find that an ideal set of tools allow us to describe our task and write our code using the same idioms.
The fact that "mapping JSON" doesn't actually involve calling "map" on JSON data is a great example of why not having generics in golang is frustrating for me.
I forgot the 3rd category, those who see the world exclusively through the functional programmer lens :-D
Mapping functionally it worth it if the transformation between A and B is regular enough. But things are dirty. Some irregularities appear and now the perfect higher-order function doesn't work nicely anymore. With the imperative approach it just means adding 1 -3 more lines.
I think you're exactly right: People who live and breathe JSON and strings don't (and probably shouldn't) care about use cases where the choice between float or double matters...
I also see this with graphics people who live and breathe small (2x2, 3x3, and 4x4) matrices don't care about "type level integers" (as Rust calls them) to make 6x9 matrices efficiently live on the stack or in an array.
> Is it possible that a boxed "Number" type would solve most of the problems?
If you mean it the way I think you do, not for me. I can easily afford to fit a billion 4 byte floats in memory, but I can't afford a billion 8 byte box pointers to a 4 byte box with an n-byte tag, and the cache locality could be horrible.
However, if Go had an elegant macro system (Rust's doesn't seem too bad), I could get by though.
Ironically generics don't necessarily solve this. C# requires multiple implementations of numeric methods because int/float/etc don't sit in a type hierarchy.
It also doesn't really work in Java either because you have to use the Object types.
Yes, it works well enough in C++. It would be nice if another language, without C++'s other pitfalls, got some of this right.
Btw, it's "ironic" that you think pointing at two broken implementations says anything about where it works. "Are folks just not aware" it's ridiculous to be snarky when arguing from a position of ignorance?
The complexity in that rust example comes from the indirection of requiring you to declare what operations you require via traits. C++ is much simpler: whatever operation you use is required.
In agreement. IMHO generics basically have one place where they work well: abstract containers. Your queues, linked lists, what-have-you can benefit.
But most other data structures? Nope. Too much of the algorithm depends on the primitive type.
And that makes generics a nice boon for computer scientists and certain library authors, but of middling benefit for day-to-day coding. If the container works, you can copy-paste-modify your way to the types you want, or automate the same in a macro or code generator. It's not beautiful but it doesn't have to be.
Having coded C# for many years now, generics are used for a lot more than just containers. There's a great many things that can be generalized. Just look at any large C# project on GitHub.
You didn't ask me, but the people I know using it are primarily writing networking or cryptographic code.
My biggest complaint about Go is the lack of generics; I bet they're just trying to get more adoption. That being said, adding generics won't eliminate all the other things I dislike about the language.
No, Go is not currently the right tool for me, but it's close, and it would be much closer with (usable) generics.
What's "so sad" is you think this Reddit style "Don't use it" imperative is an acceptable way to talk to people. You're rightfully afraid of complexity creeping into an otherwise clean language, but it would suck less if you said it better.
I don't get this idea why generics are perceived as some kind of a fad that's opposite to pragmatic coding practices. I mean, they were in the original (1983) Ada - hardly a language known for its cowboy attitude, general laxity, or "clever code".
Same here, as a security consultant reading Golang is always a pleasure. I'm pretty sure that the strongest codebases I've seen were in Golang just because of how easy it is for everyone to read the code and actually spend time looking for logic bugs instead of trying to understand what is happening.
Developers love to be able to write their magical code and their clever abstractions and often forget that their code is paying the price by becoming less clear and less readable. From a security perspective that's a huge loss.
I know that people with the right security mindset will do the right thing, and have more code where it improves clarity, or have slower code when it makes it easier to understand what is being done. But unfortunately that's a tiny minority of developers.
Developers love to be able to write their magical code and their clever abstractions and often forget that their code is paying the price by becoming less clear and less readable.
Are you saying that it has to be that way with generics? Because I would disagree with that. Cautiously applied, generics can make your code more clear, more readable and, most importantly, more type safe – and not just because of containers, see Swift’s key paths for an example.
> Developers love to be able to write their magical code and their clever abstractions and often forget that their code is paying the price by becoming less clear and less readable.
Clearly, that justifies the fact that you can't sort an array of doubles and an array of floats with the same function.
I think this argument needs to be treated with caution. I think it's possible to write bad code on top of almost any mechanism.
Certainly I've seen my share of bizarre C++ template shenanigans (bad) but I've also been able to reuse other people's really well written container classes rather than roll my own, all with strong type safety (good).
Everyone will make their own decisions about generics. Personally I find the risk of "bad generics magic" to be more tolerable than "endless shitty workarounds for not having generics".
I'm curious, what's a better solution for generics if you want some kind of container which doesn't need to care what type it contains? Is the better solution to just use the current C-style void* s everywhere and manually cast your objects to and from void* (or in Go's case, interface{}), leaving type safety to the programmer? Or is the argument that people shouldn't need to write containers, and all necessary container types should be a part of the standard library and impossible to re-implement using regular Go code? Or is code generation a good enough solution? Or are there other, nicer solutions to enable generic container types which can't be abused like Java-style generics can be?
I don't particularly care which direction Go goes in, and don't write a lot of it myself, but I'm very curious what the alternative is.
I've been writing Go for five years and I've never had a pressing need to create my own generic container type. The built-in types -- arrays, slices, maps, and channels, or some composition of them -- have proved sufficient. When I do need a new type of container, it either ends up being specialized for a specific type, or it accepts generic types via interface. Like, I need a red-black tree for this one algorithm, but do I really need a generic red-black tree that can handle any type? The answer is almost always no.
I'm fine with Go having a handful of built-in generic containers+functions. In fact, I think most people asking for "generics" would have their true needs met by adding a few more built-ins to the language. I pursued that idea here: https://github.com/lukechampine/ply
I sympathize with the programmers who really do need generics and would like to program in Go, because I love the language. But the way I see it, 95%+ of programs don't truly need generics. I like the trade-off Go makes by omitting generics, and it makes me sad that the language could change in such a radical way to meet the needs of that <5%. A language doesn't need to be perfect (or even usable) for everyone! Throwing in feature after feature to satisfy everyone just creates a culture of compromise and destroys a language's character. I want a set of narrow tools that I can master. No one ever complained that a hammer makes a terrible paintbrush. So in a broader sense I think this whole discussion reflects a wider need for good languages. We need ten languages of Go's caliber! I know the "just use a different language" argument is kinda callous, but the alternative feels worse.
The irony is that the reason the built-in types like arrays, slices, maps, and channels are useful is because they're the only types allowed to be parameterized by types in the language. It'd be interesting to see how many people would defend this choice if they were forced to work with intArrs and stringToStringMaps, etc.
I don't have any opinions on adding generics to Go because I don't have a sense of how it interacts w/ other features of the language, but having to specialize all your types to specific uses means there are very common types of libraries that are significantly harder to find in Go than in other languages, or are much less useful, or work around the lack of generics w/ interface{} casts and are significantly less type safe. I suspect that people who deny the value of generics enjoy learning about the implementation of these things and writing them themselves rather than using libraries. Don't get me wrong, there's value in that, but it's at the expense of writing libraries with stable interfaces, boilerplate-free code, large systems that can easily be refactored, etc.
It also makes it harder to actually use a type system for the value it provides: the ability to encode facts about data irregardless of their provenance. Libraries may provide interfaces that work on a certain number of presumed useful primitive types, but your own types can encode far more constraints. Without the ability to use those types you will have to care about provenance again (has this value been validated for use by this function?) or constantly reuse a normalization process to construct those values, which may introduce conversion overhead and creates more error-prone situations. Not to mention that generics also provide constraints about what a piece of code _cannot_ do. The example made famous by haskell programmers is that an "fmap" function defined as such cannot tamper with the values being mapped over, since it has no knowledge of their concrete implementation:
fmap :: Functor f => (a -> b) -> f a -> f b
Genuinely, I don't understand why you would create a static type system in a new language that didn't allow for parametric polymorphism, it's one of the most fundamentally useful features in type-checking code.
> But the way I see it, 95%+ of programs don't truly need generics.
Yes, they do. Pretty much 100% of programmers in statically-typed language need to use generic types/functions.
The "95%+" thing is just an observation that one typically needs to define type-parametrized structures of function very seldom. This is true, but it's in no way a good point against adding generics, because those remaining ~5% of cases are crucial for peaceful programming the other ~95% of times. That's why Go has built-in generic types, but as it's adoption grows, it's becomming clear they won't do any more...
> In fact, I think most people asking for "generics" would have their true needs met by adding a few more built-ins to the language.
You argued against magic and now you'd like to add some more of it to the language...
Code generation by means of Go's standard template library has worked nicely for me. Best of all, gdb sees the concrete type, so value inspection stays easy.
Only snag, minor enough, is that emacs auto-indent becomes confused by the double braces, as in "{{.templateVar}}".
I've learned to be wary of the interface type. Although the run-time overhead is negligible anywhere outside tight loops, induced debugger overhead can become a misery.
This is the same argument that gets brought up for every language feature. I remember the days when lambdas were controversial. Lambdas used to be seen as crazy complicated magic that normal programmers wouldn't be able to understand, or would abuse to make code unreadable, or would make a language unnecessarily complicated. This side of the argument eventually loses. People get used to new language features and as it turns out they are not as complicated as people initially think. At the end of the day it's nice to be able to write a generic collection.
> Programmers love generics because they enable higher-order abstractions, and programmers love abstractions
Right, because the alternative to higher-order abstractions is spending much of your time completing MadLibs-style implementation templates for solutions to solved problems instead of just including the library that contains the (higher-order) solution and focussing on solving the new part of the your problem.
This. Worked with large C# code bases that were full of leaky abstractions using generics.
Programmers thought of ever edge case, until you found that edge case that wasn’t covered in the long chain of inheritance of generics.
Point being, I agree with you, I know Go doesn’t have inheritance like C# but I’m pretty sure we, as a community, we’ll find a way to complicate everything Go related.
> I worry about generics because they let you abstract with wild abandon. They let you inject a little magic.
Not really.
> You can't chain together operators in point-free style, or add a property to every 'Object', or directly increment a string, or anything like that. In other words, it's a terrible language for code golfing. But that sort of code has no place in a production environment anyway.
> Go tries to be non-magical, and sadly that can take much of the whimsy out of programming.
That's a weird thing to say seeing that Go very much is magical... Slices/arrays are magical and so are maps, channels and channel operators, the make() function and a whole bunch of other features.
I think your understanding is filpped. Generics are not a way to add magic, they remove magic. That's the whole point. Unfortunatelly, that which one doesn't understand may seem like magic to them even though it's not.
For example, compare a channel implementation in Go and Rust. In Go, the whole thing is pretty much magic. In Rust, it's just another struct that the compiler treats in no special way. Getting familiar with the magic of Go channels is only good for working with Go channels, the knowledge is not applicable for any other part of the language nor other languages. Learning generics on the other hand gives you better understanding of numerous features in multiple languages.
The author makes some interesting arguments, but they are all predicated on the idea that generics are somehow hard to grok or use?
Having helped teach Java to many people with less than a year's programming experience at university, I refute this.
Bear in mind we're hardly talking about higher kinded types here. Generics are a simple abstraction over a lack of type information which allows you to write and maintain a lot less code; something which has been repeatedly demonstrated to reduce the incidence of bugs.
Personally, I am encouraged that the Go team is flexible enough to change their position. That gives me more confidence in the future of the language, not less.
I keep hearing people say things like "the Go team are changing their position" but I think what a lot of people missed is that the Go team were never against implementing generics in version 2. Quite the opposite in fact as they actually often said it was a consideration for Go v2.
What they repeatedly opposed was rushing generics into a v1.x build as that could break things.
The real issue is that many of the "haters" (and I hate using that term but it really was largely just individuals who decided that didn't like Go from the outset anyway so weren't exactly day to day Go developers) didn't think the slow and methodical approach was good enough.
Sometimes it feels like those who are the loudest critics of Go aren't even Go developers and instead just like to shout about how amazing their preferences are because it does xyz differently. And frankly I get a little fed up with all the psuedo-religious zealotry some have towards programming languages. It's nuts. Yet that's exactly how many of the language-specific topics end up on HN and why many on here could be forgiven for not realising that the Go team have always been open minded about changing Go; they've just been stubborn about the timelines of such changes.
This doesn't make the criticism invalid. I tried Go, found the lack of genetics / gopath / depends management too annoying, went back to other languages. I'm going to try it again after V2 is released.
If only people continuing to use X could criticise X, we wouldn't get much progress.
I’m so conflicted about this comment because I have made nearly identical comments in the past.
I think I’m going to have to change my stance on this point, however. There have been so many occasions that something about Go bothered me, and I then later discovered was a prerequisite to leveraging a much simpler programming model, that I am humbled by how much more I had to learn about programming. And I have to admit that others before me said as much, but I didn’t believe them.
Sometimes one just isn’t competent to render a useful opinion.
If you haven’t seen the movie, read the book or eaten at the restaurant, the directors, authors, and chefs should rightfully ignore your criticism. And I for one am glad the Go authors ignored criticisms like mine, I am a much better programmer now then I was then because I learned something new.
Although I will admit it was difficult at times. It’s hard to go from being an expert at something to a beginner at something you feel should be essentially the same.
I feel like you're saying the criticism has to turn out right or wrong. I don't feel that's the case. A criticism is just an opinion (based on facts or not). Ignoring them will lead one way, addressing them another. Some people will like either one of them.
I think criticism from people who stopped using X is a valid opinion even if it's ignored and if other people are happy it was ignored. You can't please everyone.
What the GP was saying is that a critique from someone who actively uses something should carry more weight than a critique from someone who doesn't use something because they happen to prefer the goals of something else already available anyway.
This was also the point I was driving at. I do respect the opinions of other developers outside of the Go community as well but as you said, you cannot please everyone. When many of the loudest critics were those who seemed to miss the point of Go or at least we're disinterested in the goals of Go, it got quite irritating to constantly read how "Go is terrible" when clearly it is a perfectly decent language for a great many developers. It's the same thing with the roadmaps, those who criticised it the most were often the developers who werent Go developers and thus didn't depend on the assurances Go offers.
I wish more programmers were as thoughtful and humble as you. Sadly my personal experience is that most developers tend to be full of themselves and their opinions without even questioning them a bit.
Complaints about gopath always feel like such a nitpick to me. Environment variables are trivial. Few Languages are as simple as Go, when it comes to development environments.
The complaint about GOPATH is not that it's an environment variable. It's that it enforces a specific directory structure that does not match how most people organize their code trees.
Instead of a project being in a folder called, say, "foo", you end up with something like $HOME/go/src/github.com/bar/foo. Then, when you install your project's dependencies, they get commingled into this structure. If you do "ls $HOME/go/src", you'll see a ton of stuff polluting your tree, all of which are arguably build artifacts, not primary sources. Very few people manage their codebases like this. At best, you put all your dependencies under a subfolder somewhere so that there's no confusion.
Go 1.11 does away with GOPATH for your project, and relegates all the dependencies to $GOPATH/pkg/mod. We are now using this new module mode, and it's a breath of fresh air compared to the previous system. This way, Go feels a bit more modern and much less gnarly.
It part of the death by papercuts for me. Sure, it's trivial, but it's the only language that requires it and I can't see any benefit of it. When working with multiple projects, it can lead to weird errors until you remember to change it too.
It's like getting a house where you have to use 2 separate switches to turn each light on/off. Is it a big deal? No. It's just weird and annoying, without an obvious benefit.
>I keep hearing people say things like "the Go team are changing their position" but I think what a lot of people missed is that the Go team were never against implementing generics in version 2. Quite the opposite in fact as they actually often said it was a consideration for Go v2.
They said that occasionally, but always dismissing any kind of proposal that wasn't theirs, including perfectly fine existing paradigms from other languages, and always giving the impression to many people that they were just paying lip service to the idea that they are OK with eventually adding Generics, or stalling for time.
The fact that it took 10 years for this to finally come as a concrete suggestion (and not even with a final delivery date or official roadmap yet), means that they were indeed stalling for time.
>Sometimes it feels like those who are the loudest critics of Go aren't even Go developers and instead just like to shout about how amazing their preferences are because it does xyz differently.
This tired argument has been said again and again. Many seasoned go developers have spoken in favor of generics over the years. And this features was #1 in Go's own developers poll year after year.
Besides, it's not like non-users of a language are automatically wrong. If anything, they could be the ones seeing its faults more objectively (e.g. people calling out Java world's obsession with overusing patterns).
It's true language changes don't seem to have been prioritized until recently. But the last big thing (package management) was probably long overdue. Before that it was mostly improving the compiler, runtime, and standard library. There's all sorts of interesting niche languages that aren't used because those ecosystem things aren't as robust...
Also I see stuff like they should just use "perfectly fine existing paradigms from other languages" on HN all the time. But from the very beginning they said that Java-style generics were undesirable (due to overhead) and C++/Rust style were also undesirable due to code size. In the end, the proposal does not choose either system and tries to enable the compiler to make trade-offs in between. It seems most inspired by CLU and a proposed future C++ feature... I genuinely don't understand why HN views this as an easy thing to just copy in, unless they view the design goals as unnecessary to begin with...
I definitely think it will be convenient to have type safety for collections that are not lists or hashmaps/sets. I also think it's convenient to have multiple return values and slices (so every list api accepts sub-lists without copying). Overall, I didn't find Go to be tremendously less convenient than Java or C#... I'm kind of curious what kind of software HN is working on, where Go is so much worse than the alternatives.
To quote the entire sentence, "It seems most inspired by CLU and a proposed future C++ feature".
Note that Stroustrup proposed C++ concepts in 2003, and they still don't exist in C++, but they might be standardized by 2020.
Also note that CLU also didn't implement compile time specialization at all which is one of their core design requirements (for performance).
I don't think this is the particular prior art most of the "generics are easy" people are referring to, anyway.
It's maybe accurate to say that the Go team could have fairly easily done Java-style parameterized types (implemented via boxing everything like Java) and they chose not to do that. They chose not to do that mainly for performance reasons (and the hit would probably be worse in Go than Java since it's more value type oriented and doesn't have a JIT).
A lot of people here seem to value expressiveness above everything, but I think it's unsurprising that that Go didn't want to make those performance compromises, since it's trying to be an alternative to Java and C++ at Google-scale.
Java-style parameterized types is not the only option and they have acknowledged that they ignored us for a long time.
"They are likely the two most difficult parts of any design for parametric polymorphism. In retrospect, we were biased too much by experience with C++ without concepts and Java generics. We would have been well-served to spend more time with CLU and C++ concepts earlier."
I'm not disputing that there's prior art for their design like C++ concepts (although C++ doesn't do run-time generics and CLU didn't do compile time). I'm just disputing the view designing and implementing it will be easy, like they could have just thrown it in there without taking substantial time away from other priorities and/or delaying the language, so anything short of providing generics day 1 is disingenuous or maybe ignorant.
Take this interview with Chris Lattner (of Swift) for comparison:
Some of the things they want in the future but just haven't had time for (more reflection, core networking libraries, etc) are things Go provided day one. If you look at the recommended way of dealing with JSON in Swift for example, it's full of boilerplate. Go also provided green threads and GC which probably adds some implementation work compared to languages that don't have them. They've also prioritized language stability to a much greater degree than Swift.
I don't know to what degree Go has succeeded with its mission inside Google/Youtube (for new projects, obviously they aren't going to re-write everything), but it seems like the Go team has more resources than it did before, which might be part of why they're more comfortable tackling major changes like this now.
If language designers didn't want to make nuanced trade offs in the design space between compile time, performance, code size, ease of use/complexity etc and sweat all the implementation details I'm sure they could operate a lot faster. Javascript might have already cornered that niche though.
> hate using that term but it really was largely just individuals who decided that didn't like Go from the outset anyway so weren't exactly day to day Go developers
I'm a day-to-day Go developer and maintain some of the most widely used Go projects out there. While I understand their wish to do things "slow and steady" there were several other missteps and double-downs by the language designers that left me with a bitter taste in my mouth.
Not to mention that Go was sold as a systems programming language, but my last 5 years of experience with the language (developing container runtimes -- which are the most canonical example of systems program you can come up with these days) tell me otherwise.
Yeah I've written some systems type stuff in it as well such as a UNIX shell and FUSE file systems. So Ive played around with syscalls and the sort in Go and frankly I'm inclined to agree with you there. I mean sure you can use Go as a systems language (as we have proven) but that doesn't mean it's the best suited language for that particular type of application.
However I don't think that's so much an issue with the lack of generics nor Java-esk exceptions but rather it's memory management and green threads. You may disagree with me though - the stuff you work on is definitely a level or two up from anything I've attempted in Go.
From a practical standpoint, you're right that the biggest issues are green threads (which had many issues where runtime.LockOSThread didn't actually do what it said on the tin -- making it impossible to use for certain namespace syscalls) and the memory management sometimes having "issues" (I found a bug several years ago where it looked like the memory manager would not free memory but would use MADV_DONTNEED -- which is less efficient than free -- but then wouldn't reuse said memory which meant that the process was killed by the kernel eventually due to memory overcommitment).
The other stuff is mostly icing on the cake in terms of whether the language is painful to use -- and having no generic container types is a perfect example of a day-to-day pain. Every time you have to sort a slice you need to implement sort.Interface (or nowadays you can use sort.Slice -- but that requires casting your slice to the right interface which requires allocating a new slice to get around missing type information).
You cannot write a single-threaded program in it (GOMAXPROCS doesn't do what you probably think it does), and the green threading until very recently didn't obey runtime.LockOSThread which meant that you couldn't even guarantee that a function would execute on the same logical thread (which is critical for certain syscalls).
cgo (which is unfortunately necessary if you have C library dependencies) has significant issues like not being able to support union conversion to a Go type -- which means that you have to write helper functions in C and call those to access unions. This is part of a more generic problem that the memory safety model doesn't lend well to systems programming (and unsafe.* is just too scary to be usable everywhere).
Oh, and the syscall package is effectively deprecated and you now have to switch everything to golang.org/x/sys/unix in order to use it properly (though one of the maintainers does nicely send PRs to projects often). This indicates to me that not enough people tested that the "syscall" package interface made sense in the 9 years before 1.0 and the stability guarantee set in (which means that the code which is very important for a systems programming language wasn't widely tested).
If you want to (for instance) use AT_FDCMD with openat(2) you won't be able to because all of the syscall bindings require file descriptors to be a uint -- despite the kernel interface using an int (and the fact that AT_FDCMD is -100 or something like that). Yet they use AT_FDCMD internally -- which means they know it is needed but they elected not to expose this to users.
It uses the "int" type for PIDs instead of using pid_t, for crying out loud. No systems programming language should do that.
> Sometimes it feels like those who are the loudest critics of Go aren't even Go developers
Perhaps unsurprisingly, those who didn't like what they saw when taking a look at Go might well have chosen to work in other languages.
This might be a basis on which to prioritize criticism -- certainly the criticisms of those who use a language are more important to those managing it than the complaints of those who don't use it -- but it's not a reasonable basis on which to disqualify criticism.
And to point a criticism back: sometimes it feels like some of Go's loudest defenders don't choose to offer the discussion much beyond that response ("you're not really using it because if you were you'd see that the pain point you're describing is imaginary or not that bad or has plans to make plans to be fixed").
I don't begrudge anyone who likes it their happiness using it. I know people who really do like it. But I know people who gave it a fair shot didn't like it. Mine was 9 months of part-time work on a project before I settled into the conclusion that most of MJD's faint praise of Java (https://blog.plover.com/prog/Java.html ) plus a few extra downsides applied to Go. Including, yes, the arguably generic-shaped hole.
I also don't begrudge the stewards of the language their own solution to that problem or their own timeline for it. Presumably a similarly generous spirit should apply to complaints continuing to register. If the pothole is still in the road, it isn't surprising that observations about it continue to roll in even after plans to develop plans to fix it may have been announced.
> This might be a basis on which to prioritize criticism
Maybe if the language was unsuccessful then yes. But given the popularity in f Go it's fair to say the concerns of its existing user base should take precedence over those that may not use the language regardless.
> certainly the criticisms of those who use a language are more important to those managing it than the complaints of those who don't use it -- but it's not a reasonable basis on which to disqualify criticism.
But as this topic has demonstrated, those concerns haven't been disqualified out if hand either.
> And to point a criticism back: sometimes it feels like some of Go's loudest defenders don't choose to offer the discussion much beyond that response ("you're not really using it because if you were you'd see that the pain point you're describing is imaginary or not that bad or has plans to make plans to be fixed").
I disagree that happens. In my first post I made the arguement about language stability (not breaking things). Others have made the arguement of simplicity and/or readability as well. I think the real issue is some people don't consider those good enough reasons so dismiss the counterarguments out if hand. Which is obviously an opinion you're entitled to but you have to remember that it's the stability and simplicity that has made Go popular in the first place so those points actually do matter to a great many of us.
The thing is, there is already C#, Java, Rust, C++, and a great many other languages out there that have rich ecosystems and expressive semantics, Go has gone in a different direction by being a boring language. This direction clearly divides people though.
> Sometimes it feels like those who are the loudest critics of Go aren't even Go developers
I have been writing Go exclusively for almost 3 years, (I still maintain some legacy J2EE applications, and live in a Java/C# town) -- this has been true anecdotally 99% of the time. There's the one-off who actually does use go regularly.
Then again, I witness this same phenomena in the JS framework battles, even within my own company. Vue vs React, where neither of them have ever used each others and built an actual production application to evaluate it. This is just the editor wars over again.
The Go team should have considered generics for Go 1.0 during the 0.x phase.
It's 2018. We've done three decades of computer science since C, Oberon, or whatever the Go developers drew inspiration from. If you want your language to be taken seriously for large scale app development in the 2010s you need strong static typing, and a sophisticated, parametric type system. No exceptions. Advanced typing constructs like dependent and higher-kinded types are a bonus.
We've also done enough computer science to know there are much better ways to relieve the programmer of the burden of doing all memory/resource management explicitly than a tracing GC. So using a tracing GC in an ostensible systems programming language was another huge boner.
Go 1.x was a cascade of bad decisions. It's like Java was in 1995: a language with training wheels that had to change directions entirely when real programmers started using it to build real applications.
There’s precedent that Google’s brand doesn’t make people magically swallow programming languages without thinking: Dart.
I think it’s disingenuous to reduce Go success to some Google marketing operation. I dislike Google as a brand in the way they design products and their weak privacy stance, but I love Go nonetheless.
Some dart ramblings.
Seems to me, that the Chrome team is responsible for Dart demise and its later flutter inspired resurgent.
Flutter was started by people in the chrome team, as an experimental project called sky.
With regards to making it possible to include the dart vm the oilpan project was started.
Oilpan, the replacement of reference counting in Blink, with a tracing garbage collector, this would have made it possible for dart vm to sit along side v8 in chrome.
Oilpan was started in 2013 and instead of taking months, it took years, I think it finally arrived in 2016.
Here is a dart video from io 2013 announcing oilpan.
https://youtu.be/huawCRlo9H4
Go is also similar to languages like limbo/newsqueak. None of those are popular(many might not have even heard of them). So part of success definitely goes to being successfully marketed as a google product.
The main focus of go is to be as simple as possible.
It's not that they don't known how to write complicated programs under the hood and keep it simple outside.
They want to keep the programming language simple even under the hood where ever possible.
Consider implementing a feature in hurry just because many people were asking, one needs to maintain them for the rest of their life even if they hate it so much, because of backward compatibility.
It can also be the root cause for many problems.
If generics make language complicated, then it's better they don't implement it in the first place.
I’m a fellow generics pessimist. Having come to Go from Java some 6 years ago I initially found the lack of generics a curious, sometimes frustrating omission.
After years of using Go daily and reading a long succession of posts on the go-nuts mailing list wherein Rob Pike, et al, repeatedly and staunchly defended the lack of generics as necessary – even beneficial – in the pursuit of designing a small, readable, coherent language, I’ve since come to believe that decision was entirely correct.
Perhaps nobody technically promised there would never be generics in Go’s future, but it’s undeniable that they run contrary to those stated design goals. As currently proposed, they will add considerable complexity to the language in exchange for questionable benefit.
I’m not sure the world needs another kitchen-sink language that eschews clarity for the unrestrained addition of features. If you want that, there are already plenty of options for you.
The only person from the original three creators who still actively takes part in the language's evolution seems to be Robert Griesemer. Ken Thompson is retired, and Rob Pike seems to have moved to other projects (Upspin?). Robert Griesemer is still active, but mostly "debugging" the language specification.
The impression I get is that Russ Cox is pretty much the Chief Designer right now (pardon the Sergey Korolyov reference). The original three authors have said that they would not add a feature to the language if one of them opposed it, which is how they kept it simple. I wonder if rsc has anyone to say "no" to him.
That's elaborate. More elaborate than expected for Go.
I was expecting something more like explicit parameterized types. Go already has those - maps and channels are parameterized types. You just can't define new parameterized types. Extending Go to allow those would be a smaller step. That doesn't get you overloaded functions, though.
The designers of this wanted the ability to define a new function F(T x) which works for any T upon which a known list of functions can be applied. Then they wanted that to work for separate compilation. So there's all that "contract" stuff to carry the info about what F needs from T. Seems clunky. It violates the rule "You should never have to tell the computer something it already knows." It's clear how the constraints they set upon themselves lead there, but it's still clunky.
This design does not support template metaprogramming or any other form of compile-time programming.
Whew! That's a relief.
"We expect that most people will not write generic code themselves, but many people are likely to write packages that use generic code written by others."
In other words, maybe it is a little too complicated.
It violates the rule "You should never have to tell the computer something it already knows."
Not really: the contract is an explicit list of requirements with API compatibility guarantees for future versions. The computer cannot guess that from the current implementation.
I have written about 10+ medium and big projects in Go for production over last 6 years. They were written mostly for CI/CD, APIs, cron jobs and various pipelines in e-commerce and consumer electronics domains. Have personally never needed generics or have felt that I can improve the code by using generics. I also have quite a bit of Java experience so I do know why you would need generics, but it just never came up in Go. Maybe my opinion will change with the new proposal getting implemented, but at this point I don't see how.
It's impossible to defend the lack of generics in golang in the face of the fact that two of the larger open source projects both invented their own generics implementations.
It’s weird to see complaints about code generation causing a too big code base at compile time when code generation is exactly what a compiler will do behind the scenes for generics.
Not necessarily, specialisation is only argubly required for unboxed types. Java actually implements "Generics" without any specialisation at all, it boxes all the primitive types (rather controversially).
The C#/.NET solution, while very good, has some problems too. By implementing generics so deeply into the runtime, it becomes very difficult to enhance the support in the future, for example adding higher-kinded types (which the F# folks have long been asking for).
I actually quite like the Haskell approach of having a "Specialisation" annotation. I believe Scala has this too.
As someone that works on gVisor, I disagree with this w.r.t. our project. i.e., the existence of our go_generics tool is not an obvious indication that we need generics in the language.
A bit of history: this tool was originally created specifically for two packages, a linked list package, and a tree-based data-structure package. Both of these used an interface for the entry types, but this had two main drawbacks:
1. Interface assertions and conversions are (often) not free. These packages are used heavily in our critical system call handling path and this was costing on the order of a couple hundred nanoseconds in the critical path (total cost of the critical path is ~1500-4000ns depending on the syscall).
2. Escape analysis does not work across interface methods, requiring heap allocation of pointer arguments. This was less of an immediate problem for the initial creation of go_generics, but annoying to work around in the critical code where it mattered.
The go_generics tool solves both of these problems by generating versions of these data structures with concrete types. However, neither of these problems require generics to solve. General compiler and toolchain improvements could solve both. In fact, I imagine that (1) is greatly improved already (we were solving this problem in the Go 1.5 timeframe).
To this day, our code base has a grand total of 5 generic templates:
segment [1], ilist [2]: These are the two packages referenced above.
seqatomic [3]: Synchronization primitive for sequence count protected values. Not safe to use interfaces.
bits [4]: This is the typical "multiple integer types" problems. IMO, this use is overkill, there are only ~20 lines of code to copy.
pagetables Walker [5]: Used in critical //go:nosplit //go:noescape context, where most runtime calls are unsafe.
Given our experience with, and very narrow use of our go_generics tool, I'd actually rather Go not add generics and continue to use specialized code generation in the places we really need it.
I do recognize that there are a lot of other cases where people legitimately want generics and I am not fundamentally opposed to the draft proposal, but this was my long-winded way of saying that our go_generics are not an obvious argument in favor.
Exactly. Like all ideological arguments it completely ignores actual real-world evidence. Somebody should sit down and analyze real codebases and understand and quantify how much these projects would be improved by real generics and proper exception handling. My (anecdotal) experience and speculation suggests that you'd see massive reduction in complexity and lines of code (especially when you consider code-gen).
But then this is part of a larger problem of the community which isn't motivated by evidence or basic pragmatism but rather strict adherence to a philosophy.
Changing the language specifically requires experience reports. If that's not real world evidence i'm not sure what is.
The problem with the wider go community is they are usually not driven by evidence, but adherence to a philosophy. eg, its not possible to write large programs without generics.
> Somebody should sit down and analyze real codebases and understand and quantify how much these projects would be improved by real generics and proper exception handling
This function will only accept a pair of integers. In order to perform addition on floats or int64s or any other number type, you would need a separate function.
Go has the concept of an empty interface (interface{}) which can accept any type. But this is not really a generic because you have to assert that it is a specific type in order to be sure what it actually is at runtime. This function for example, will not compile:
In a language like Java, I could write this function to accept any type for which the + operator is valid, and it would still be type safe at compile time. I would not need to assert the types like I did above.
> In a language like Java, I could write this function to accept any type for which the + operator is valid, and it would still be type safe at compile time. I would not need to assert the types like I did above.
That's because + is a java built-in unavailable to normal types. Usually something like an add method is used instead. It does not detract from the underlying point.
In a static language, functions, classes, and types are often parametrized by other types.
For example, with a function: imagine you wrote a `sum(array)` function. In Ruby that's not a problem, because array can contain any types, as long as they support the + operator (aka duck typing). Any function can also return any type of object.
In a static language, however, an array that holds floats has a different type than an array that holds integers or an array that holds strings. The return value of the function sum() will also be different, depending on the type of the array, because if it's an array of floats, the sum will be a float.
So generics allow you to sort of implement duck-typing in static language:
T sum(array<T>) // Pseudocode
Now the sum function will take an array of any type T, and return a result of the same type T.
Similar idea for classes and other types. The classic example is a container. A linked-list in Ruby can contain any type or mixture a types, since the language is fully dynamic. However, in a static language, a linked-list of strings has a different type than a linked-list of ints. So you write something like (pseudocode):
class List<T>
{
T head_val() { return head.val; }
}
Now you write the List<T> class once, and use it for any type T, so it's DRY when you may have any number of types T that your program stores in a list.
Awesome, so this makes sense. So I guess people are against it because they like the explicit simple nature of Go rather than some sort of abstraction magic?
I'm a bit sad about the lack of acceptance for pluralism in widely used languages. The effort to get Go in line with other languages just seem so disproportional.
Go is a tool, it's not a religion. People are allowed to criticise it, and they don't have to temper their criticism just because the Go team or Go users have a different opinion. Go is made by Google it's not like it's some kid's school project.
I agree. The real problem is they're trying to add generics to Go and make Go 2 backwards-compatible with Go 1. Doing both generates the complexity, just like with Java 5. Generics in a language are good, but Go will cross the line from simple to bloated as soon as these generics are added. To remain a simple language with generics, Go 2 would need to give up backwards compatibility with Go 1, something which the suits at Google would veto if any techies there actually tried it.
Yet another language tries to bolt on generics after the fact and finds that doing so results in awkwardness. Note to language designers: if you are creating a general purpose language, you will eventually need to add generics. You can either do it right at the beginning, or else you have to bolt it on at the end. Rust gets kudos in this regard for having well designed generics from the beginning.
Rust does have generics. There's little value in constructing a distinction between "templates", "generics" and "parametric polymorphism", at least not for the parent comment, which was likely intended to mean "Rust has a well designed system for working with 'unknown' types". (Also, maybe you could be specific about what you think the difference is.)
Go also doesn't have subclasses, so that doesn't seem relevant.
I understand what these words mean... At least, I understand the variety of meanings they have: they're not always used to mean the same thing. For instance, people will rightly say that Java doesn't strictly have parametric polymorphism because of dynamic type casts, but they will say it has generics. Other people might draw a distinction between generics and templates, with the former being constrained (with type classes or similar) and the latter being macro-like.
However, I don't think these are actually particularly relevant to the original comment (and, I really think that Rust probably satisfies "generics" more than "parametric polymorphism", because one can also do generic casts).
Every time I read a discussion about generics in Go, I remember discussions on the Small/SmartEiffel list where people argued that Eiffel doesn't need this or that because it can be emulated with a feature already available in the language. Well, Eiffel is history now.
Programmers love generics because they enable higher-order abstractions, and programmers love abstractions -- code that isn't DRY is like an itch we need to scratch. Programmers also hate special cases, because they feel restrictive and inconsistent: I recall quite a few people protesting that Go's 'range' keyword shouldn't be restricted to a handful of built-in types, but rather should support any type implementing an Iterator interface.
But Go was not designed to be perfectly consistent or to enable high-order abstractions. It just aims to be productive at scale, and that often means restricting the programmer. Go tries to be non-magical, and sadly that can take much of the whimsy out of programming. You can't chain together operators in point-free style, or add a property to every 'Object', or directly increment a string, or anything like that. In other words, it's a terrible language for code golfing. But that sort of code has no place in a production environment anyway. It's "clever" code that gives the programmer a little dopamine hit when he writes it, and his co-workers a headache three months later. Those are misaligned incentives.
So I worry about generics because they let you abstract with wild abandon. They let you inject a little magic. And I fear that programmers will be seduced by their love of magic, and I'll be the one who ends up paying the price.