Rendered at 06:01:14 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
DarkNova6 7 hours ago [-]
Step by step, Go is now learning the hard lessons every other language has learned over the last 20 years. The fact that despite their best efforts, their propositions look like everone else is a surprisingly refreshing affirmation of status quo.
somekyle2 6 hours ago [-]
I respect "we're going to try not to add features until we have to and have a plan that we like" as an approach to a practical language, vs "we're going to plan to support everything normal modern languages have".
It isn't the best approach to language design in theory, but they did try to build a language that isn't impossible to change, and to be fair the mix of language features they chose didn't have any good more successful examples to borrow from, so waiting for enough practical experience to make the relevant trade-offs on practical grounds isn't the bare naivety that some people seem to take it as.
DarkNova6 5 hours ago [-]
I totally get that. Java is my favorite language for exactly that reason. They are a deliberate "late mover language" and adopt what other languages have proven right.
But looking at how adamant Go used to be on the whole "No Generics" stance and the path and the troubles they are having... it all seemes so preventable?
farfatched 4 hours ago [-]
I think it was.
Go without generics was ok. I say that as a fan of generics in other languages.
There is room for languages with simpler feature sets.
It takes a strong leader (and the good and bad that comes with that) to continue to deny features. Go had that. Does it still? (I genuinely don't know.)
I'm not sure what Go's value proposition is now.
Developers can't pick it up in a week, as it used to be. Maybe that's okay too, since Go already has critical mass.
kamma4434 18 minutes ago [-]
> Go without generics was ok. I say that as a fan of generics in other languages.
Well kinda. I found it very annoying every time you want to print a sorted map which keys are strings that you could not abstract the operation. It felt like paying a stupidity tax. (And I quite like go. It’s the only language where write once run everywhere actually works. Cross compiling for all targets is as easy as creating a local binary)
LVB 1 hours ago [-]
I don’t feel like the learning curve has shifted dramatically for mainstream development. Besides generics—which tend to not show up that often in app code even now—I can’t think of any substantial language change since I Iearned it in 2014. (Well, modules, but that’s more tooling.) Where I do see the shock is jumping to def of a std lib function and landing in indecipherable type soup.
ako 3 hours ago [-]
Go value proposition for me, when developing with Claude code: 1) fast compilation times, faster iterations, 2) user friendly to install, one single binary per platform, 3) feels safer than JavaScript/typescript, which have too many npm vulnerabilities. Go comes with more functionality included, less need to use 3rd party libraries.
farfatched 1 hours ago [-]
That's fair. In addition, I'd add the great compatibility guarantees, and a community that makes easy-to-learn libraries.
win311fwg 4 hours ago [-]
Does it? It was only preventable by not releasing Go, which isn't exactly the greatest of solutions.
As you know from when Go was first broadcast to the world, they hadn't figured out how to make generics fit[1]. It is not like they weren't trying. Ian Lance Taylor is regarded for beginning work on generics before anyone outside of Google had even heard of Go. "In short: not yet"
What was surprising is that after Go was released as an open source project, nobody across the whole wide world ever came up with a workable solution. You can sense the optimism in that original announcement that later turned to "Go might never get generics" dispair. As you'll recall, it finally took convincing Philip Wadler (of Haskell and Java generics fame) to help. Without that lucky break, which almost certainly never would have happened if Go was still some basement project used by nobody, Go probably still wouldn't have generics.
The problem is that when you don’t have a cohesive picture of how things fit from the outside, it becomes really hard to evolve the language in a way that makes sense and doesn’t break existing code. Data storage and languages share this unique property that the past impacts your decisions and what makes is possible in the future in a way pure logic doesn’t struggle with.
Sleaker 5 hours ago [-]
And this is -exactly- one of the reasons they didn't accept or continue with a sane proposal for more ergonomic error handling. I like go, but this was the one thing that I kept feeling like I was wasting so much time dealing with.
Well, better late than never. Stuff like sets or a typed heap is long overdue. Maybe they'll even add iterator API for database/sql results this decade too (something like my pull request for sqlx https://github.com/jmoiron/sqlx/pull/990 but maybe more polished)
Also having stuff like a concurrency limiter (instead of doing weird var limiter chan struct{} and using it as `limiter <- struct{}{}` and `defer <-limiter`) as a library function in `sync` package would be great too.
astonex 8 hours ago [-]
There is actually a way to do this with errgroup provided you can work with func() error signature
But I guess the issue is that oftentimes, it is more of a distributed system coordination issue?
What use case do you have in mind?
kamma4434 26 minutes ago [-]
Welcome to Java, where Collections are the bread and butter of every programmer since 1998.
While you are at it, I humbly suggest to add composable streams too, that we had in 2014, and that make working with collections way more pleasant.
(Jokes aside, in Java it is very nice that you can start with a generic ArrayList and turn it into a linked list with one single keyword change and no code needs changing, or that you can turn any collection into a synchtonized or readonly doppelganger with one line of code. Collections are nice and I miss them in Golang)
alfiedotwtf 26 minutes ago [-]
No reference to Canadian Aboriginal characters in the comments :(
valcron1000 6 hours ago [-]
22 years late, but better late than never.
jiehong 9 hours ago [-]
Well, finally!
I wish they wouldn’t mix mutation methods in there, but ok.
athorax 10 hours ago [-]
On one hand I'm glad to see this being added, but its becoming obvious building generics into the language as-is just isn't a good fit. Hopefully Go v2 can solve this at a more foundational level while still allowing interop or easy porting form current go code.
nasretdinov 9 hours ago [-]
Why isn't it a good fit? The design prioritises readability of code that uses generics vs writing generic code (which is in line with the overall language philosophy). It also does it in a way that doesn't impact compilation speed or add too much complexity, which is also in line with the language philosophy.
If you don't like the fact that not all of standard library has been refactored to support the new language features -- I think it'll come over time. Once these features land into the standard library they can't be taken away, so the language authors take their time to make sure it's designed well.
I don't see anything going obviously wrong here.
TheDong 5 hours ago [-]
I agree it's not a good fit.
Go is a language built for people who don't wish to learn any math or CS theory, for people who don't wish to be "computer scientists" but rather "grug-brained programmers".
The new datatypes will mean having to read things like "sz := sx.Union(sy)", and the union operation between sets is too math-like, and thus makes it less readable.
"Advanced" data-types, like sets and heaps, only make code more readable to okay programmers, it makes code less readable to the average go programmer. To the average go programmer, a union operation is more readable as a for loop which does not have any math-y sounding name at all.
rattlesnakedave 4 hours ago [-]
some of us that have to work with people like you are happy that you're constrained by Go.
bananamogul 9 hours ago [-]
I thought the status of Go v2 was that the devs weren't saying it will never happen, but also were not working on it. There was some momentum that seemed to sputter out once generics were added.
"when should we expect the Go 2 specification that breaks old Go 1 programs?
The answer is never. Go 2, in the sense of breaking with the past and no longer compiling old programs, is never going to happen. Go 2 in the sense of being the major revision of Go 1 we started toward in 2017 has already happened."[1]
Granted, this post was more about if there will someday be a Go that will break backward compatibility. But it sort of answers where Go 2 is as a side effect.
First they said they won't add generics. They violently defended that decision. Developers bent over backwards to make it work without generics. Some even wrote long blog posts defending Rob Pike's decision. Some wrote posts arguing against it. Now the Golang team adds them. Why waste so much developer time? It's not a few months. It's several years. This industry is seriously a clown show tbh.
pphysch 8 hours ago [-]
At this point, the "damage" has been done and even skeptics of generics/iterators should applaud this proposal since it means burying some of that added complexity behind straightforward stdlib interfaces.
Unless they are just here to complain, of course.
pstuart 8 hours ago [-]
Core members of the language team (Robert Griesemer and Ian Lance Taylor) argued for generics from the very beginning. The team resisted because they didn't know how to do it without sacrificing speed of compilation.
Rob Pike did a lot of defensive work to deflect it but I'll quote him here on the issue when he said "There are no plans for generics. I said we're going to leave the language; we're done":
"I meant there are no plans for generics. That's not the same as saying
we plan not to do generics. It just means we don't have a plan."
You take it as a conspiracy against developers when it's really a small team trying to find their way the best they can.
improgrammer007 8 hours ago [-]
It's an utter waste of time for developers, business, everyone. Millions of lines of Go code have been written without generics. Libraries have been written. Support, maintenance, cost, etc. This is why one shouldn't pick languages that regress in features from the get go. Now developers will waste time migrating the code to stdlib. This is not solving problems. This is doing tech for the sake of doing tech. Wasting everyone's time. Remember that code is not the deliverable.
wbl 6 hours ago [-]
Nothing was broken in the change: there is no need to migrate.
jppittma 7 hours ago [-]
Claude will migrate your whole repo in an hour.
kokada 2 hours ago [-]
I assume they will eventually add those to `go fix` too, so you don't even need Claude for the most basic refactors (the more complex ones, sure).
pstuart 7 hours ago [-]
I'm confused by what language regression you are referring to.
> Now developers will waste time migrating the code to stdlib
Why? If it works now it works. Refactoring tools exist, and LLMs make this trivial.
You just seem to hate the language and everything about it, and that's your right but I think your arguments are specious and ignore that most language that go mainstream evolve and that comes with tradeoffs (python2 -> python3, rust in general, etc).
And as far as deliverables go, the code is absolutely foundational to that and Go was made to be maintainable by codemonkeys such as myself.
Anyway, the language and the changes noted in the OP apparently aren't for you and that's ok -- there's plenty of languages out there for everyone's taste.
5 hours ago [-]
saturn_vk 8 hours ago [-]
Violently?
rapnie 8 hours ago [-]
Too much. More like vehemently. Perhaps parent meant to say that.
improgrammer007 8 hours ago [-]
Yes, just used the word for emphasis :-)
amazingamazing 8 hours ago [-]
What’s wrong with meeting demand?
improgrammer007 8 hours ago [-]
Nothing is wrong. What was wrong before when they vehemently argued against adding it? Is that design principle/core value lost now?
ninkendo 7 hours ago [-]
Reminds me of when Apple finally moved the iPhone to USB-C… they defended lightning for the longest time, but then when when they finally did the switch, all I could think was “the last N years [0] of lightning accessories I’ve bought are now ewaste”… once it became clear USB-C was going to be the future, every year they weren’t using it was another year of future ewaste building up.
Every year go didn’t have generics was another year of workarounds and tech debt building up that would have otherwise been able to be written the right way from the start. Unlike ewaste though, it’s probably impossible to quantify.
[0] I’d probably peg N as the years between when they gave MacBooks USB-C ports for charging, up until the iPhone got them. It’s clear Apple knew they were gonna need to move to it eventually… every year in that period represents a year of lightning cables people bought that could have been still-useful USB-C cables today.
kokada 1 hours ago [-]
Technical debt, sure. But you could argue the same for any new feature added to a programming language, without having X you will need to workaround with Y, generating technical debt.
To be clear, I was one of those people that only started to use Go after generics. But for most of my projects, generics is less than 1% of the code base, so it is not like lack of generics was a huge issue. I think it is more of a problem for libraries in general.
amazingamazing 7 hours ago [-]
It is not a monolith, maintainers change? Why does it matter so much to you anyway? You can choose not to use generics
It's sad that such basic stuff took this long. If we're lucky we might even see a `Map` function in our lifetimes.
PrimalPower 10 hours ago [-]
The beauty of go is YAGNI. Language design makes it much harder for people so do stupid and cute things.
During my design process if I start realizing that I’m missing maps, More expressive Types, Or more complex polymorphism. I ask myself if I really need those things.
If I really do. I move off of go.
That’s the beauty of the language. Go does not need more complicated language features because it’s can handle the majority of trivial software work without unnecessary complexity.
The language does not need to solve complicated problems.
brokencode 9 hours ago [-]
Ok, and what if you realize you want these things a million lines in? Do you still move off of Go?
Generic programming isn’t some fancy research language feature like dependent types. It’s just a bare minimum feature in any modern typed language.
It’s perplexing that after C# and Java both notably shipped without generics initially then added them later that they decided to ship Go without generics.. only to end up adding them later.
tialaramex 8 hours ago [-]
I guess it's possible that C# and Java taught the wrong lesson (you can add this later) and that actually reduced the impetus to ensure Go shipped generics in 1.0
It is also entirely fair to say there's a lot of complexity here and so there's a risk you exceed your complexity budget which for Go as I understand it was very slim. It is a possible a Go 1.0 with more generics doesn't take off because too many people bounce off the extra complexity and so a decade later it's an obscure thing Google made once that has a few fans but not much adoption.
Or that extra complexity means Go 1.0 ships five years later, after Rust 1.0 has given people an appetite for better performance and better safety and its sharpest corners have already been knocked off.
typical182 7 hours ago [-]
From what I've seen, I don't think the core Go team was ignoring the lessons of Java or C#.
Here's a sample quote from Russ Cox from 11 years ago on this site: [1]
We have spoken to a few true experts in Java generics and each of them has said roughly the same thing: be very careful, it's not as easy as it looks, and you're stuck with all the mistakes you make. As a demonstration, skim through most of http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.ht... and see how long before you start to think "was this really the best way to do this?"
And of course, they asked other experts for help, including Philip Wadler (of Featherweight Java and Haskell fame): [2] [3]
We’ve been thinking about generics since work on Go began, and we wrote and rejected our first concrete design in 2010. We wrote and rejected three more designs by the end of 2013. Four abandoned experiments, but not failed experiments, We learned from them, [...]
Last year [2018] we started exploring and experimenting again, and we presented a new design [...] and we’ve been working with programming language theory experts to understand the design better.
FWIW, C# generics are way better (ergonomics) than the Java ones (due to type erasure), so Java waited too much. History is more complicated though for Java, since it was either having some generics vs having none. See [this](https://softwareengineering.stackexchange.com/questions/1766...) for a lengthier discussion.
lilbigdoot 1 hours ago [-]
Yes, .NET had the benefit of coming second and using F#, which was originally essentially OCaml 4 on .NET, as it's testing grounds. Same for Async
I am considering .NET for one of my compilers backends because of the reified generics. .NET can even pass around an object with generic methods that get specialized via JIT at runtime each time it sees a new data type (with reference types sharing implementations). Which also ties back to having true value types
It's a shame it took so long for .NET core to come around because even today the platform carries a reputation for being windows first which hasn't really been true for many years now
metaltyphoon 46 minutes ago [-]
C# was meant to have generics in day one, but it didn’t make the cut and it was introduced on the next version (2.0)
xtracto 8 hours ago [-]
Maybe at some point it may even get the ternary conditional operator or a proper exception handling syntax.
Those things that Golang defenders say "dont make sense" until it's implemented, then 'it always made sense" and of course it's a good idea.
interf4ce 5 hours ago [-]
I'd welcome the ternary conditional operator but I hope exception handling never gets introduced (at least as I'm used to it from Java, C#, JS, etc.).
Exceptions should not be conflated with regular error handling, especially when they're allowed to bubble up from anywhere. I very much appreciate that functions that can return errors force the caller to deal with them, for the most part. True exceptions can already be thrown with panic, although I actually find it incredibly rare that I need to reach for that tool. I'd argue that when people talk about exceptions they almost always actually want "unhappy path" error handling.
Now, if Go wanted to add a sleeker way to handle those errors, similar to Rust's approach, I'd be very interested. Minimum three lines for every call to a function that returns an error does get a bit verbose, arguably hurting readability. A little sugar could improve readability without making error handling implicit.
cyberpunk 7 hours ago [-]
So your argument is that they should have gotten it exactly right first time and stuck to their guns?
I mean come on. The Golang team created a useful language people use for building real things — it’s easy to work with especially on large teams, and when the lack of generics turned out to be a pain point in the end (after years of production reality) they understood what the community wanted and actually… added them..
Now what, it’s not good enough?
No one forced you to use go.
No programming language is perfect. I personally find the language has served me well.
And after being so very pro generics myself, i actually find myself not even really using them that much apart from calling the slices module etc which has them under the hood anyway…
brokencode 6 hours ago [-]
Nope, I’m saying that the language that was seemingly designed with either an ignorance of or disdain for the hard-won lessons from decades of prior programming languages.
There was even this condescending attitude that Google engineers couldn’t understand fancy languages anyway, so they had to dumb Go down.
My criticism really isn’t even about Go itself. Yeah it’s improving, which is great.
My criticism is about this anti-intellectual attitude that has permeated the entire Go community since its inception. It’s like hearing that college is a waste of time from people who never graduated high school.
inigyou 6 hours ago [-]
my criticism is that if we make every language identical, we might as well only have one language. (C++, obviously)
There is room for a language without generics. There was a language without generics. Now there is not.
brokencode 6 hours ago [-]
Well here’s the thing on generics. Go always had generics since the very beginning. It’s just that only certain built-in types had them.
Slices, maps, and channels all were generic from day one. Same with functions like append, copy, etc.
Your criticism makes no sense unless you think Go shouldn’t have had generics from the start, which it did.
metaltyphoon 42 minutes ago [-]
I think when people mention generics, they don’t mean builtins from the language but a language construct for generics.
improgrammer007 8 hours ago [-]
This. One should never pick a language that regresses in terms of features from the get go.
bborud 7 hours ago [-]
This is also why people eventually leave languages. Satisfy everyone’s wishes for long enough and you get lots of different styles of doing things and … «wow look at that much simpler language over there; let’s try that instead»
inigyou 7 hours ago [-]
Go's whole philosophy was to keep the language simple. It's a shame they're abandoning that now and becoming the next C++/Zig/Rust/Java
troupo 23 minutes ago [-]
They threw many babies out with the bathwater chasing yhe rather unspecified "simplicity" and making parts of the lsngusge more complex to use as a result.
wannabe44 10 hours ago [-]
Map function leads to poor code in Go. Function literals are verbose and inlining is far less agressive. It simply isn't the way of the language. Even python shuns map and filter in favor of comprehensions. A for loop is more readable than the lambda soup.
TheDong 5 hours ago [-]
> A for loop is more readable than the lambda soup.
Let's assume you have a slice of strings you want to uppercase. Which of the following is more readable?
uppercased := slices.Map(inputSlice, strings.ToUpper)
// or
uppercased := make([]string, 0, len(inputSlice))
for _, s := range inputSlice {
uppercased = append(uppercased, strings.ToUpper(s))
}
Let's say you want to parse a bunch of user-given inputs into durations, surely the following is more readable?
I think map functions lead to cleaner code when used like the above, and a lot of for loops end up falling into those patterns.
wlamartin 2 hours ago [-]
I think in a language designed around FP ergonomics and expectations there's a lot to like about it.
In your second example, as a retrofit, I find myself asking "when does MapErr stop consuming the input list?", "which error gets returned if multiple errors could be returned?", "is it a errors.Joim situation", "if there are multiple errors how do I map them to the failed elements in parsed", "if I did want each parse to have either success or fail (think Result type) how would I represent this generically in a language that favours multiple return types"
There's a lot going on with that example that a for loop makes explicit and flexible for other choices.
ninkendo 8 hours ago [-]
IMO it depends on the language… to me, map/collect/etc are useful in languages that have the concept of immutable data, because you can initialize an array with a single call chain, and be sure that nothing after that can modify it.
What I’ve seen in the “for loop” approach that I can’t stand, are things like (pseudo code)
var a = []
for x in coll1 {
a.push(foo(x))
}
do_stuff_with(a)
// … further down the function
for y in coll2 {
a.push(bar(y))
}
do_other_stuff_with(a)
Reading code like that, is the first call to do_stuff_with(a) a bug, because it’s not fully built from both coll1 and coll2 yet? Or is the second call to do_other_stuff_with(a) a bug because a now contains more stuff than the developer probably thought? Can I safely move both loops next to each other, or does that break something subtle? If I need to pass a to a new function, where can I safely do this? Before or after I add from coll2? (In my actual times seeing this, a is really a map of cached key/values or something, and it’s kinda ok that the contents were different each time it was used, but subtle bugs emerged…)
IMO the sane way to do it is to just not incrementally mutate things like that, and stick with giving things a single place where they’re defined and initialized. Go doesn’t really help you here because there’s no such thing as immutable data. So just adding Map/collect or whatever doesn’t really buy you much.
tialaramex 7 hours ago [-]
Often the mutation might have performance benefits.
Of course if you're Rust the stdlib and compiler might conspire to optimise an operation you wrote which reads as non-mutating into an actual mutation which was faster.
This is another benefit of the "destructive move". If I consume X and spit out Y, the X is gone, so it's OK if secretly I just mutate X and tell you that's Y now.
tyre 1 hours ago [-]
Python uses comprehensions because whitespace sensitivity was a horrendous language decision, then couldn’t find a way to support multiline lambdas.
Comprehensions are not a well-designed feature but a consequence of poor design.
troupo 8 hours ago [-]
map functions are very useful for iterating over collections. Most functions iterating over collections are useful, because a lot of the data you're commonly dealing with is collections. And most collections are generic.
> Function literals are verbose and inlining is far less agressive.
> Even python shuns map and filter in favor of comprehensions.
That's the problem of the language design. And Python isn't the best language to turn to for language design
> A for loop is more readable than the lambda soup.
A for loop shoving modified items into a temp variable with append() that is then returned is less readable than a map function transforming data. Too bad Go decided to turn lambdas into unreadable soup.
10 hours ago [-]
pstuart 10 hours ago [-]
Sounds like a win to me. I'd assume that many PL decisions come with with costs of either implementation or readability, and Go's conservative evolution takes that into account. Disclaimer: not a PL designer, just a codemonkey.
It's not a perfect language, and the process has not been without its pain points, but work like this makes the language more powerful and I feel like I get my money's worth when I use it.
Yes, I'm a Go fanboy but I'm not interested in language wars (e.g., yes Rust is more performant and correct but sometimes "good enough" is good enough).
cyphar 10 hours ago [-]
On the other hand, design decisions made under one set of constraints can become problematic when you add new features that don't play as well with earlier design decisions.
For a concrete example, the fact you cannot define custom methods for external types in Go (a pre-1.0 design decision) means that you cannot write proper compile-time generic code to deal with some generic wrapping type (dumb example -- getting a sum of the perimeters of a generic set of shapes in an externally-defined collection type) -- you are forced to work around it with runtime type-switching. There are all sorts of arguments you can make about simplicity but this is an objective shortcoming of Go that is directly caused by generics being added to Go long post 1.0.
My take on this is that despite selfishly wanting more from Go for many years myself, adding more stuff to Go at this late stage is just slowly chipping away at Go's uniqueness with features that make a large number of people using them unhappy. (For example, while they make some APIs nicer, iterators turn a basic logic bug that is impossible with for loops -- forgetting to stop iterating when the loop has a "break" -- into a runtime panic. And they really suck to compose.)
aatd86 9 hours ago [-]
__For a concrete example, the fact you cannot define custom methods for external types in Go (a pre-1.0 design decision) means that you cannot write proper compile-time generic code to deal with some generic wrapping type (dumb example -- getting a sum of the perimeters of a generic set of shapes in an externally-defined collection type) -- you are forced to work around it with runtime type-switching. There are all sorts of arguments you can make about simplicity but this is an objective shortcoming of Go that is directly caused by generics being added to Go long post 1.0.__
I'm a bit intrigued by this: how would that work with compatibility between libraries, separate compilation and what not?
My first instinct is that it would not be a good idea but I might be missing something..?
cyphar 3 hours ago [-]
In Rust this is solved by defining a local trait and methods implemented for a trait are only available if the trait is imported. This is in contrast to Go where methods are not namespaced at all. But there are almost certainly many other ways of solving the problem.
I was a little surprised how often I ran into this issue when using libraries that started defining structures as generic containers.
pstuart 10 hours ago [-]
Fair enough. Primeagen did a video recently renouncing his love of Go over the concerns you've raised.
That said, I'm assuming much of this "wobbly" functionality will live in libraries and will not be common in day to day coding (much as I've seen with generics so far). It's all optional after all.
I use Go because it's simple, capable, and been my prime language for over a decade and that skill enables me to be valuable enough for a decently paying job.
If I were younger with more energy and time on my hands I'd likely be a rustacean but I think I can ride out my career on this. YMMV.
cyphar 9 hours ago [-]
I've maintained one of the major container runtimes (which is written in Go) for over a decade as well, I think I have a less rosy view of Go than you but I still think it's a nice language all things considered and still use it for some projects.
pstuart 9 hours ago [-]
My goal is to be pragmatic about the issue (and everything else).
When I started with it I saw it as the love-child of C and python.
Then saw it as a successor of Java.
I recognize that Rust will win this contest (although my understanding is that async still has sharp edges to round down). I'm old and tired and have lost the energy and focus for exploring new languages and believe that despite its shortcomings it will continue to have enough value to be useful.
Good enough will do for me. I tip my hat to the all the other PL contenders and wish them all well (except Java -- PTSD from that destroyed any love I had for the language).
wbl 6 hours ago [-]
Rust will not "win" when it targets a much different niche, any more than C won against Java or Scheme.
troupo 8 hours ago [-]
> (dumb example -- getting a sum of the perimeters of a generic set of shapes in an externally-defined collection type)
There's a much more common example: dealing with external APIs that return JSON. Their response is trivially wrapped in Req<T>. Pre-generics Go would force you to write tedious duplicated boilerplate code for each request type, or just "cast to void*" with interface{} and hope for the best.
> adding more stuff to Go at this late stage is just slowly chipping away at Go's uniqueness
Uniqueness should not be the goal of a language.
tikhonj 9 hours ago [-]
"Everything is a trade-off" assumes you're on the Pareto frontier.
Go is... very much not on the Pareto frontier of programming language design.
whateveracct 9 hours ago [-]
> "Everything is a trade-off" assumes you're on the Pareto frontier.
100%. it pisses me off so much that i have to hear this idea thrown around constantly.
fragmede 9 hours ago [-]
Somewhat. The trade off is it can be made more complex at the cost of its simplicity. You can argue that some of those tradeoffs don't in fact have to be made because it's not on the Pareto frontier, eg generics, but we'd have to have a specific discussion about specific language features instead of vague generic discission about the language.
pstuart 9 hours ago [-]
I find language wars tiresome at this point. There was no claim that it was a frontier language, or even that it was a great language. My point was that the Go authors do try to consider the impact of enhancements to the language, which I think is appreciated by many that use it.
roundwego 3 hours ago [-]
A bit too late frankly. The snobbish arrogance didn’t help.
9 hours ago [-]
akiarie 9 hours ago [-]
Honestly the more I see this the less I like Golang. Generics was the worst thing ever added to the language. We're making it easier for library builders and harder for ordinary code to be written.
Quitschquat 2 minutes ago [-]
This used to be the popular take not long ago. Now it's grayed out and unpopular.
woodruffw 8 hours ago [-]
Is there a categorical division between “library builders” and “ordinary code”? That’s not one I’ve heard before.
(I’m aware of the difference between application and library code, but every large codebase I’ve ever worked in is a mixture of both.)
jerf 6 hours ago [-]
Do you use Go? Because it sounds like no. In the last four years I've encountered I think two uses of generics in the wild in a 3rd party library. Both quite sensible.
Anyone moaning about how it's ruined Go is either not using Go, or simply impossible to please.
If you hate Go... and hey, you do you, I've got my own list of languages I don't like... find a better complaint because this one is simply nonsensical. "It doesn't let me map/filter/reduce" or "it doesn't fix lock problems" or other similar complaints have some grounding in reality, but this one is just absurd. Generics aren't used enough to be "the worst thing ever". Fears about how Go would instantly turn into a language full of generics that take generic arguments that take generic arguments have proved to be wrong.
Working on any collection is easier with generics. Working with anything that accesses data in uniform way (APIs, SQL etc.) is easier with generics.
pphysch 8 hours ago [-]
This proposal is literally about making it easier for ordinary code to be written without defining new generic types or iterators.
znpy 9 hours ago [-]
Looking forward to see G2EE being released as a specification /s
throw_m239339 8 hours ago [-]
Nothing wrong with J2EE back then if you were building a certain kind of entreprise application. EJBeans actually took care about a lot of business complexity for a n-tier application, it's just that the whole XML crap was absolutely egregious.
Go can barely parse an XML document natively so don't worry about G2EE I'd say...
I'm more worried about what have become of professional Javascript serverside these days, it's just nuts how people have managed to make it as complex as J2EE... Nextjs, Typescript,JSX, React, compilers, transpilers, ... and none of that stuff solves enterprise business logic...
But looking at how adamant Go used to be on the whole "No Generics" stance and the path and the troubles they are having... it all seemes so preventable?
Go without generics was ok. I say that as a fan of generics in other languages.
There is room for languages with simpler feature sets.
It takes a strong leader (and the good and bad that comes with that) to continue to deny features. Go had that. Does it still? (I genuinely don't know.)
I'm not sure what Go's value proposition is now.
Developers can't pick it up in a week, as it used to be. Maybe that's okay too, since Go already has critical mass.
Well kinda. I found it very annoying every time you want to print a sorted map which keys are strings that you could not abstract the operation. It felt like paying a stupidity tax. (And I quite like go. It’s the only language where write once run everywhere actually works. Cross compiling for all targets is as easy as creating a local binary)
As you know from when Go was first broadcast to the world, they hadn't figured out how to make generics fit[1]. It is not like they weren't trying. Ian Lance Taylor is regarded for beginning work on generics before anyone outside of Google had even heard of Go. "In short: not yet"
What was surprising is that after Go was released as an open source project, nobody across the whole wide world ever came up with a workable solution. You can sense the optimism in that original announcement that later turned to "Go might never get generics" dispair. As you'll recall, it finally took convincing Philip Wadler (of Haskell and Java generics fame) to help. Without that lucky break, which almost certainly never would have happened if Go was still some basement project used by nobody, Go probably still wouldn't have generics.
[1] https://youtu.be/rKnDgT73v8s?t=3267
https://en.wikipedia.org/wiki/Conway%27s_law
https://pkg.go.dev/golang.org/x/sync/errgroup#Group.SetLimit
What use case do you have in mind?
While you are at it, I humbly suggest to add composable streams too, that we had in 2014, and that make working with collections way more pleasant.
(Jokes aside, in Java it is very nice that you can start with a generic ArrayList and turn it into a linked list with one single keyword change and no code needs changing, or that you can turn any collection into a synchtonized or readonly doppelganger with one line of code. Collections are nice and I miss them in Golang)
I wish they wouldn’t mix mutation methods in there, but ok.
If you don't like the fact that not all of standard library has been refactored to support the new language features -- I think it'll come over time. Once these features land into the standard library they can't be taken away, so the language authors take their time to make sure it's designed well.
I don't see anything going obviously wrong here.
Go is a language built for people who don't wish to learn any math or CS theory, for people who don't wish to be "computer scientists" but rather "grug-brained programmers".
The new datatypes will mean having to read things like "sz := sx.Union(sy)", and the union operation between sets is too math-like, and thus makes it less readable.
"Advanced" data-types, like sets and heaps, only make code more readable to okay programmers, it makes code less readable to the average go programmer. To the average go programmer, a union operation is more readable as a for loop which does not have any math-y sounding name at all.
"when should we expect the Go 2 specification that breaks old Go 1 programs?
The answer is never. Go 2, in the sense of breaking with the past and no longer compiling old programs, is never going to happen. Go 2 in the sense of being the major revision of Go 1 we started toward in 2017 has already happened."[1]
Granted, this post was more about if there will someday be a Go that will break backward compatibility. But it sort of answers where Go 2 is as a side effect.
[1] https://golang.google.cn/blog/compat
Unless they are just here to complain, of course.
Rob Pike did a lot of defensive work to deflect it but I'll quote him here on the issue when he said "There are no plans for generics. I said we're going to leave the language; we're done":
"I meant there are no plans for generics. That's not the same as saying we plan not to do generics. It just means we don't have a plan."
You take it as a conspiracy against developers when it's really a small team trying to find their way the best they can.
> Now developers will waste time migrating the code to stdlib
Why? If it works now it works. Refactoring tools exist, and LLMs make this trivial.
You just seem to hate the language and everything about it, and that's your right but I think your arguments are specious and ignore that most language that go mainstream evolve and that comes with tradeoffs (python2 -> python3, rust in general, etc).
And as far as deliverables go, the code is absolutely foundational to that and Go was made to be maintainable by codemonkeys such as myself.
Anyway, the language and the changes noted in the OP apparently aren't for you and that's ok -- there's plenty of languages out there for everyone's taste.
Every year go didn’t have generics was another year of workarounds and tech debt building up that would have otherwise been able to be written the right way from the start. Unlike ewaste though, it’s probably impossible to quantify.
[0] I’d probably peg N as the years between when they gave MacBooks USB-C ports for charging, up until the iPhone got them. It’s clear Apple knew they were gonna need to move to it eventually… every year in that period represents a year of lightning cables people bought that could have been still-useful USB-C cables today.
To be clear, I was one of those people that only started to use Go after generics. But for most of my projects, generics is less than 1% of the code base, so it is not like lack of generics was a huge issue. I think it is more of a problem for libraries in general.
During my design process if I start realizing that I’m missing maps, More expressive Types, Or more complex polymorphism. I ask myself if I really need those things.
If I really do. I move off of go.
That’s the beauty of the language. Go does not need more complicated language features because it’s can handle the majority of trivial software work without unnecessary complexity.
The language does not need to solve complicated problems.
Generic programming isn’t some fancy research language feature like dependent types. It’s just a bare minimum feature in any modern typed language.
It’s perplexing that after C# and Java both notably shipped without generics initially then added them later that they decided to ship Go without generics.. only to end up adding them later.
It is also entirely fair to say there's a lot of complexity here and so there's a risk you exceed your complexity budget which for Go as I understand it was very slim. It is a possible a Go 1.0 with more generics doesn't take off because too many people bounce off the extra complexity and so a decade later it's an obscure thing Google made once that has a few fans but not much adoption.
Or that extra complexity means Go 1.0 ships five years later, after Rust 1.0 has given people an appetite for better performance and better safety and its sharpest corners have already been knocked off.
Here's a sample quote from Russ Cox from 11 years ago on this site: [1]
We have spoken to a few true experts in Java generics and each of them has said roughly the same thing: be very careful, it's not as easy as it looks, and you're stuck with all the mistakes you make. As a demonstration, skim through most of http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.ht... and see how long before you start to think "was this really the best way to do this?"
And of course, they asked other experts for help, including Philip Wadler (of Featherweight Java and Haskell fame): [2] [3]
We’ve been thinking about generics since work on Go began, and we wrote and rejected our first concrete design in 2010. We wrote and rejected three more designs by the end of 2013. Four abandoned experiments, but not failed experiments, We learned from them, [...]
Last year [2018] we started exploring and experimenting again, and we presented a new design [...] and we’ve been working with programming language theory experts to understand the design better.
[1] https://news.ycombinator.com/item?id=9622417
[2] https://go.dev/blog/experiment
[3] https://go.dev/blog/generics-next-step#acknowledgements
I am considering .NET for one of my compilers backends because of the reified generics. .NET can even pass around an object with generic methods that get specialized via JIT at runtime each time it sees a new data type (with reference types sharing implementations). Which also ties back to having true value types
It's a shame it took so long for .NET core to come around because even today the platform carries a reputation for being windows first which hasn't really been true for many years now
Those things that Golang defenders say "dont make sense" until it's implemented, then 'it always made sense" and of course it's a good idea.
Exceptions should not be conflated with regular error handling, especially when they're allowed to bubble up from anywhere. I very much appreciate that functions that can return errors force the caller to deal with them, for the most part. True exceptions can already be thrown with panic, although I actually find it incredibly rare that I need to reach for that tool. I'd argue that when people talk about exceptions they almost always actually want "unhappy path" error handling.
Now, if Go wanted to add a sleeker way to handle those errors, similar to Rust's approach, I'd be very interested. Minimum three lines for every call to a function that returns an error does get a bit verbose, arguably hurting readability. A little sugar could improve readability without making error handling implicit.
I mean come on. The Golang team created a useful language people use for building real things — it’s easy to work with especially on large teams, and when the lack of generics turned out to be a pain point in the end (after years of production reality) they understood what the community wanted and actually… added them..
Now what, it’s not good enough?
No one forced you to use go.
No programming language is perfect. I personally find the language has served me well.
And after being so very pro generics myself, i actually find myself not even really using them that much apart from calling the slices module etc which has them under the hood anyway…
There was even this condescending attitude that Google engineers couldn’t understand fancy languages anyway, so they had to dumb Go down.
My criticism really isn’t even about Go itself. Yeah it’s improving, which is great.
My criticism is about this anti-intellectual attitude that has permeated the entire Go community since its inception. It’s like hearing that college is a waste of time from people who never graduated high school.
There is room for a language without generics. There was a language without generics. Now there is not.
Slices, maps, and channels all were generic from day one. Same with functions like append, copy, etc.
Your criticism makes no sense unless you think Go shouldn’t have had generics from the start, which it did.
Let's assume you have a slice of strings you want to uppercase. Which of the following is more readable?
Let's say you want to parse a bunch of user-given inputs into durations, surely the following is more readable? I think map functions lead to cleaner code when used like the above, and a lot of for loops end up falling into those patterns.In your second example, as a retrofit, I find myself asking "when does MapErr stop consuming the input list?", "which error gets returned if multiple errors could be returned?", "is it a errors.Joim situation", "if there are multiple errors how do I map them to the failed elements in parsed", "if I did want each parse to have either success or fail (think Result type) how would I represent this generically in a language that favours multiple return types"
There's a lot going on with that example that a for loop makes explicit and flexible for other choices.
What I’ve seen in the “for loop” approach that I can’t stand, are things like (pseudo code)
Reading code like that, is the first call to do_stuff_with(a) a bug, because it’s not fully built from both coll1 and coll2 yet? Or is the second call to do_other_stuff_with(a) a bug because a now contains more stuff than the developer probably thought? Can I safely move both loops next to each other, or does that break something subtle? If I need to pass a to a new function, where can I safely do this? Before or after I add from coll2? (In my actual times seeing this, a is really a map of cached key/values or something, and it’s kinda ok that the contents were different each time it was used, but subtle bugs emerged…)IMO the sane way to do it is to just not incrementally mutate things like that, and stick with giving things a single place where they’re defined and initialized. Go doesn’t really help you here because there’s no such thing as immutable data. So just adding Map/collect or whatever doesn’t really buy you much.
Of course if you're Rust the stdlib and compiler might conspire to optimise an operation you wrote which reads as non-mutating into an actual mutation which was faster.
This is another benefit of the "destructive move". If I consume X and spit out Y, the X is gone, so it's OK if secretly I just mutate X and tell you that's Y now.
Comprehensions are not a well-designed feature but a consequence of poor design.
> Function literals are verbose and inlining is far less agressive.
> Even python shuns map and filter in favor of comprehensions.
That's the problem of the language design. And Python isn't the best language to turn to for language design
> A for loop is more readable than the lambda soup.
A for loop shoving modified items into a temp variable with append() that is then returned is less readable than a map function transforming data. Too bad Go decided to turn lambdas into unreadable soup.
It's not a perfect language, and the process has not been without its pain points, but work like this makes the language more powerful and I feel like I get my money's worth when I use it.
Yes, I'm a Go fanboy but I'm not interested in language wars (e.g., yes Rust is more performant and correct but sometimes "good enough" is good enough).
For a concrete example, the fact you cannot define custom methods for external types in Go (a pre-1.0 design decision) means that you cannot write proper compile-time generic code to deal with some generic wrapping type (dumb example -- getting a sum of the perimeters of a generic set of shapes in an externally-defined collection type) -- you are forced to work around it with runtime type-switching. There are all sorts of arguments you can make about simplicity but this is an objective shortcoming of Go that is directly caused by generics being added to Go long post 1.0.
My take on this is that despite selfishly wanting more from Go for many years myself, adding more stuff to Go at this late stage is just slowly chipping away at Go's uniqueness with features that make a large number of people using them unhappy. (For example, while they make some APIs nicer, iterators turn a basic logic bug that is impossible with for loops -- forgetting to stop iterating when the loop has a "break" -- into a runtime panic. And they really suck to compose.)
I'm a bit intrigued by this: how would that work with compatibility between libraries, separate compilation and what not? My first instinct is that it would not be a good idea but I might be missing something..?
I was a little surprised how often I ran into this issue when using libraries that started defining structures as generic containers.
That said, I'm assuming much of this "wobbly" functionality will live in libraries and will not be common in day to day coding (much as I've seen with generics so far). It's all optional after all.
I use Go because it's simple, capable, and been my prime language for over a decade and that skill enables me to be valuable enough for a decently paying job.
If I were younger with more energy and time on my hands I'd likely be a rustacean but I think I can ride out my career on this. YMMV.
When I started with it I saw it as the love-child of C and python. Then saw it as a successor of Java.
I recognize that Rust will win this contest (although my understanding is that async still has sharp edges to round down). I'm old and tired and have lost the energy and focus for exploring new languages and believe that despite its shortcomings it will continue to have enough value to be useful.
Good enough will do for me. I tip my hat to the all the other PL contenders and wish them all well (except Java -- PTSD from that destroyed any love I had for the language).
There's a much more common example: dealing with external APIs that return JSON. Their response is trivially wrapped in Req<T>. Pre-generics Go would force you to write tedious duplicated boilerplate code for each request type, or just "cast to void*" with interface{} and hope for the best.
> adding more stuff to Go at this late stage is just slowly chipping away at Go's uniqueness
Uniqueness should not be the goal of a language.
Go is... very much not on the Pareto frontier of programming language design.
100%. it pisses me off so much that i have to hear this idea thrown around constantly.
(I’m aware of the difference between application and library code, but every large codebase I’ve ever worked in is a mixture of both.)
Anyone moaning about how it's ruined Go is either not using Go, or simply impossible to please.
If you hate Go... and hey, you do you, I've got my own list of languages I don't like... find a better complaint because this one is simply nonsensical. "It doesn't let me map/filter/reduce" or "it doesn't fix lock problems" or other similar complaints have some grounding in reality, but this one is just absurd. Generics aren't used enough to be "the worst thing ever". Fears about how Go would instantly turn into a language full of generics that take generic arguments that take generic arguments have proved to be wrong.
Working on any collection is easier with generics. Working with anything that accesses data in uniform way (APIs, SQL etc.) is easier with generics.
Go can barely parse an XML document natively so don't worry about G2EE I'd say...
I'm more worried about what have become of professional Javascript serverside these days, it's just nuts how people have managed to make it as complex as J2EE... Nextjs, Typescript,JSX, React, compilers, transpilers, ... and none of that stuff solves enterprise business logic...