Golang proposal: container/: generic collection types
Posted by jabits 3 days ago
Comments
Comment by DarkNova6 2 days ago
Comment by somekyle2 2 days ago
Comment by DarkNova6 2 days ago
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?
Comment by win311fwg 2 days ago
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.
Comment by pjmlp 2 days ago
"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."
https://go.googlesource.com/proposal/+/master/design/go2draf...
Comment by win311fwg 2 days ago
But one would think that of the billions of people in the world that someone outside of the Go team would have had that familiarity to quickly change that, but no proposals came from that direction for over a decade and a lot of begging. I suppose those who had the chops were too busy constantly posting on HN about how Go doesn't have generics to lend their expertise. Priorities.
It's great to reflect back in hindsight about where you failed, but it's always easy to see where you failed after someone else finally shows you the way. The simple fact that the Go team did not have the technical capability to be able to add generics at the time remains. Being able to create a relatively popular programming language (or set of popular programming languages for some of them) certainly does not imply that you are a programming language expert. There may even be an inverse correlation. Those who show to be programming language gods appear most likely to end up creating esoteric languages that end up on the pile of languages that remain unknown and unused.
Comment by pjmlp 2 days ago
Without the adoption success of Docker and Kubernetes adding wind to Go's sails, Go would have landed on the island.
They were not lacking in skills, rather politics, and there is a public talk from Rob Pike where he mentions even the Go 1.18 generics would not have landed if it was for him.
Comment by win311fwg 2 days ago
Exactly. You might say that Wirth, Mössenböck, and Winterbottom had some programming language chops, bringing some new ideas to the world, which is in line with the observation that those with programming language skills end up creating languages nobody uses. In fairness, Pike was involved in Limbo, but his only claim to fame was pressuring Winterbottom to add garbage collection to Alef, thus Limbo.
> Without the adoption success of Docker and Kubernetes adding wind to Go's sails, Go would have landed on the island.
Fun story, but no. Those projects chose Go because Go had already gained traction and was thought, in that moment in time, that it was going to be the way of the future.
It is true that Docker is often credited as the first major success story, but Hugo, etcd, InfluxDB, etc. were all released around the same time, never mind the long list of projects that never gained popularity. The language was already being widely used by the time Docker showed up. Go didn't need a major success story to get on the radar. It had Google's marketing power behind it, or at least that's why most believe it was able to become popular without having any breakouts to its name.
> They were not lacking in skills, rather politics
These are not separable concepts. Politics are the result of lack of skill.
Comment by pjmlp 2 days ago
Lots of languages are full of dead projects that never gained notoriety.
In fact, YouTube downloader which is another "success" only happened, because once again, it was a gopher that forced a RIG, it was working perfectly fine.
Comment by win311fwg 2 days ago
Ad hominem is a logical fallacy.
> Go was barely proven on the language adoption battlefield when Go heads preverted the direction Docker and Kubernetes were going, it wasn't even 1.0.
Exactly. All these projects chose Go many years before anyone, outside of Docker, had heard of Docker. This idea that Go was some kind of underground language until Docker and Kubernetes arrived at which point everyone jumped on the bandwagon is simply not true. Go was already quite popular by the time Docker was released, as evidenced by a whole lot of other projects that became popular themselves being released at the same time. Those projects wouldn't have been able to have been released when they did if they waited for Docker's success to jump on the Go bandwagon.
> Lots of languages are full of dead projects that never gained notoriety.
No there aren't. Not where the language is on the heap of languages nobody has ever heard of. Oberon-2 and Limbo didn't fail because they never found their "Docker", they failed because nobody used them in the first place.
Comment by pjmlp 1 day ago
Comment by typical182 2 days ago
Small correction, I think:
My understanding is the core Go team was already well on its way to doing generics before Philip Wadler became involved, though he and his colleagues were later very helpful.
From Wadler's "Featherweight Go" paper [1] (the first draft of which seems to have been uploaded to arxiv.org circa May 2020):
Recently, the Go team mooted a design to extend Go with generics [Taylor and Griesemer 2019], and Rob Pike wrote Wadler to ask: Would you be interested in helping us get polymorphism right [...]
and:
It took us several tries over many months to formalise it correctly.
Well before that, doing generics was one of the main focuses in the GopherCon keynotes/announcements in 2017 and 2018 by Russ Cox [2].
The August 2018 generics design [3][4] was very detailed, and my recollection is most people in the Go community at that point believed the core Go team was going to make generics happen, even if the team and community were still iterating on the exact design.
Of course, that design improved over time, including thanks to Wadler and crew, but I think it would be overstating things to say that probably nothing would have happened without Wadler.
[1] https://arxiv.org/pdf/2005.11710v1
[2] https://go.dev/blog/go2draft
[3] https://go.googlesource.com/proposal/+/master/design/go2draf...
[4] https://go.googlesource.com/proposal/+/master/design/go2draf...
Comment by farfatched 2 days ago
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.
Comment by ako 2 days ago
Comment by farfatched 2 days ago
Comment by hwc 2 days ago
I don't think any of the new additions to the library have seriously made the learning curve more difficult. I suspect that most projects don't use templates at all.
There have always been parts of the standard library I'm not familiar with. when I read code that uses it, I just go read the documentation. Now the standard library is just larger.
Comment by kubanczyk 1 day ago
Ah, so you haven't looked into json/v2 yet.
Comment by kamma4434 2 days ago
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)
Comment by LVB 2 days ago
Comment by shikck200 2 days ago
Comment by TZubiri 2 days ago
It's a very practical simplification since 99% of the time, that's what we use. 'It's in the language, we just don't use it' does sound like copeful technicality, until it isn't and it's an actually impactful difference.
Comment by vlovich123 2 days ago
Comment by Sleaker 2 days ago
Comment by win311fwg 2 days ago
While the proposed slightly reduced the token count, saving the average typist approximately 1 second of time if they don't have an autocomplete editor that types it for them, it doesn't change anything about the mental model where the real time is actually spent, so is it really sane?
Worse, it is dependent on the error type, but errors are not always of the error type. Not even Go's own standard library consistently returns errors using the error type, never mind all the other crazy things you find in the wild.
That doesn't sound sane at all.
It is true that Go not having any real kind of superpositions or side channels makes stealing popular ideas from other languages impossible (it already has both well-known error handling methods that do not depend on those properties). But a sane proposal would be designed with that in mind.
Comment by ndriscoll 2 days ago
Of course if you want something less specific to errors, we already have do-notation as an excellent example to look at. And you can't say that's more complex for users than what they did with iterators.
Comment by sethammons 2 days ago
Yes! You have to think through and account for edge cases. "Might not" is the same as "might." Those branches might execute.
I have been on many projects over 25 years of software development. Projects that take error handling seriously tend to be better all around. Happy-path coding is naive.
Comment by ndriscoll 2 days ago
Comment by win311fwg 2 days ago
There is no case where you would ever trivially send the error up the stack.
First, if your function doesn't have any meaningful information to add, you need to seriously question why the function exists. It is almost certainly not adding anything of value and the function that originally produces the error can just as well be called directly. No need to trivially pass an error when the useless "middleman" function doesn't exist.
Second, even if you still decide that a function that doesn't have worthwhile information is still worthwhile having, if you pass someone else's error straight through then you become forever coupled to the implementation of someone else. If you, for example, pass a SQLite error and then later decide you want to use Postgres, now you have to emulate SQLite errors every time Postgres produces an error, bringing you back to square one while also having one hell of a confusing API. Unless you hate other programmers and are purposely trying to make their life a living hell, the only sensible approach is to produce new error values at each step so that you can maintain stable sentinels/types.
A good language solution for errors might make it look like you are trivially passing an error up the stack while using some other method to attach the necessary metadata, like we see in some other languages, but nobody has yet figured out how to make that fit into Go. It is generally agreed upon that Go would greatly benefit from this, but until someone rolls up their sleeves and solves it...
Comment by ndriscoll 2 days ago
You do not need to have new wrappers at every level. That's what interfaces are for. e.g. if all you're going to do is retry up to N times and log, and you don't need special handling, you just call String() or whatever. But you still want libraries to return domain errors so that you can handle them if you need to, or for tests, etc. And you can have some layer in the middle to make like SQLError or DatabaseError.
Right I'm saying Scala does exactly that, and has libraries that e.g. propagate stack traces across fibres automatically and invisibly. There's prior art to observe. It's not some mysterious unsolved problem.
Comment by win311fwg 2 days ago
Right, and conceptual tasks will almost always have information that is usefully attached to an error. There are exceptions, but even in those exceptions you still have to ensure you don't leak implementation details, so you still cannot trivially pass the error through.
> Your business process might not care which task had an error
That's true, but you would never write upstream functions with the business process in mind. That is for the downstream functions to determine. Doing so would couple an upstream function to the business process, making it difficult (maybe even impossible) to use again when the next business process wants to reuse it, which would be complete insanity. A function is written to provide what makes the function useful for the sake of the function itself. It is up to the caller to decide what facets of that are useful for the business process being implemented downstream.
> You do not need to have new wrappers at every level.
You absolutely do need them. The language can work to hide that they are being created, but that does not preclude the necessity of them. This is most definitely a solved problem in other languages, but it remains that nobody has figured out how to take those ideas and apply them in Go. What was done in another language cannot be lifted wholesale and simply dropped into Go. The world is not quite so simple. It remains an unsolved problem in Go.
And understandably so. Who has time to work on solving the problem when there are comments on HN complaining that nobody has solved it yet to be made?
Comment by kbolino 2 days ago
Maybe so, but for JVM languages, this "trivially" includes a stack trace. Go doesn't offer that for normal errors and there was no clear path to getting it. The need for contextual clues when debugging, and the community and then stdlib settling on error-wrapping as the way to do it, makes "simply return err" not a sane default anymore.
Comment by ndriscoll 2 days ago
In a language that takes error handling seriously, you have domain types for errors, type checking for exhaustive error handling, and you get info like a stack trace out of the box.
Comment by kbolino 2 days ago
There is nothing special about an error in Go, and there is no type hierarchy to anchor them against anyway. An error can be a struct{} carrying no value at all, or simple integer with no way to carry context, etc. Error-wrapping is the solution that has been devised to work within these constraints, but there's no obvious way to compose that with the various ideas around reducing boilerplate. For a point of comparison, Rust's anyhow crate was able to square this circle by adding a blanket trait impl for Context, but Go has no equivalent for that. (Also, Rust's ? operator is implemented using unstable components, allowing the details to evolve even while the feature itself is usable in stable Rust, something which also has no equivalent in Go).
As to the lack of discriminated unions, I agree wholeheartedly. There have been some proposals recently to get them into the language, with various levels of conservatism and cohesion with the rest of the language, but a lot of them are faltering on the "every type must have a well defined zero value" issue (yet another design decision that doesn't necessarily have anything to do with errors).
Comment by ndriscoll 2 days ago
I had to argue about this years ago with a coding standard pushing to define local variables at the top of functions and zero initialize them in C. Why default to null pointers or worse (invalid ints/structures) when you could make use of uninitialized variables be a compiler error instead!?
Comment by 9rx 2 days ago
That may seem out of place in the world you live in, but Go was created in a world where Python, Ruby, and Javascript were the languages all the cool kids were using. Go was intended to be the language for C++ programmers who had to use C++ for performance reasons but wished they could use Python instead.
Insanity, perhaps, but Go was designed to solve a problem for the world it lived in. You don't get to choose your circumstances. For those living in paradise, they can use the languages designed for their world.
Comment by kbolino 2 days ago
Personally, I'm fine with discriminated unions always allowing "nil" in Go, since IMO the most sensible way to implement them anyway is as a special case of interfaces. Not everyone feels the same way, though.
Comment by win311fwg 2 days ago
It does offer that for normal errors if you pass those errors via exceptions. Although you're right that using exceptions for error handling is pretty weird and generally avoided in Go, much to the chagrin of Java developers.
The Upspin experiment, which eventually shaped the error additions that did make it into Go, showed a clear path to getting it in errors themselves, but it also revealed that nobody actually uses them when available. The research found that even with stack traces attached, developers didn't bother looking at them. Thus why that bit didn't make it into Go.
Comment by kbolino 2 days ago
Comment by win311fwg 2 days ago
Exactly, which said proposal does nothing to improve upon. Despite the reduction in characters it looks exactly the same as the traditional way, offering no way to improve how one think about errors. That proposal was clearly thrown out there just to try and appease the "Go doesn't have error handling" crowd without any thought into what would actually make the language better. The goal may be noble, but that particular proposal was rightfully abandoned.
> The error boilerplate as it exists interrupts the actual logic
Huh? No matter what logic you throw at the problem, there is no escaping that error handling is part of the actual logic. And it is, by far, the most important piece of the logic. Engineering is all about dealing with failure modes.
I'll grant you that there is a category of problems known as scripting tasks where error states simply can mean letting the program crash and allowing the user to clean up afterwards, but Go is clearly not trying to be a scripting language.
Comment by ndriscoll 2 days ago
My experience with high level banking and low-level networking applications is that the default path for error handling is that you want to bubble up to central handlers the vast majority of the time. There's a reason why exceptions are a popular language feature; they're just hard to make work with fibres. With scripting you often don't want to handle errors at all. Just set -euo.
Comment by flanked-evergl 2 days ago
Comment by hwc 2 days ago
Yes, there are occasionally really good reasons to use an ordered map or a priority queue, but I haven't needed either in years.
Comment by JamesSwift 2 days ago
Comment by bheadmaster 2 days ago
The most important "features" of Go are fast compilation, orthogonality of features, and easy development.
Their propositions may "look like everyone else" if you're only looking at surface syntax, but there's a reason why it took 10 years to design an acceptable generics proposal, and the result is that compiling generic-heavy code is almost as fast as compiling plain Go.
Go being strict about features it accepts is not an ideological thing, but a practical one. If Go could have all the features in the world while still being easy to use, fast to compile, and internally coherent, it would. But it can't (see: C++, C#, Python).
Comment by gritzko 2 days ago
Go used very particular shortcuts to make its GC'd runtime lightweight enough and the language simple enough. But, due to Conway's law or something else, it was destined to become a corporate-focused elephantine language, gradually.
I remember 10 years ago I believed that Go lacked something like STL. Later I changed my opinion cause I started to realize how much overhead red-black trees and suchlikes introduce. Especially in GC'd systems. This abstraction Jenga may indeed look shockingly absurd when you see all the moves in the system, not just your layer.
In performance-critical cases, I personally use ABC, a C dialect with solid containers. Those are basically chunks of RAM with fixed-bit-layout records in them. Vectors, hash maps and hash sets, heaps and queues are all just arrays. If we only needed that, we might have stayed with C. Solid containers require no malloc() and no GC. Can mmap them and you have a database. Can send them over the network as-is. That is the ultimate 80/20 thing here. LMDB is another example.
But if you keep adding features, 80/20 is no more and your Go becomes an uglier Java or Rust.
Comment by elktown 2 days ago
I find it funny reading sometimes how my country learned to conform on something that was previously a bit unique and people are like ”yeah, even they figured out it doesn’t work!”. No, you dingus, the people that wanted to conform were just elected. It worked perfectly fine otherwise.
Comment by vander_elst 2 days ago
Comment by epolanski 2 days ago
Comment by gadflyinyoureye 2 days ago
Comment by nasretdinov 3 days ago
Comment by nasretdinov 3 days ago
Comment by astonex 2 days ago
https://pkg.go.dev/golang.org/x/sync/errgroup#Group.SetLimit
Comment by inigyou 2 days ago
Comment by theowaway213456 2 days ago
Comment by arccy 2 days ago
Comment by aatd86 3 days ago
What use case do you have in mind?
Comment by smithcoin 2 days ago
Comment by troupo 3 days ago
Comment by silverwind 3 days ago
Comment by PrimalPower 3 days ago
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.
Comment by brokencode 2 days ago
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.
Comment by tialaramex 2 days ago
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.
Comment by typical182 2 days ago
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
Comment by danieldc 2 days ago
Comment by lilbigdoot 2 days ago
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
Comment by metaltyphoon 2 days ago
Comment by pjmlp 2 days ago
"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."
https://go.googlesource.com/proposal/+/master/design/go2draf...
Comment by typical182 2 days ago
It doesn't seem particularly damning to me, though, especially written by Russ Cox in hindsight. It's almost a truism that if you had spent less time on approaches that didn't pan out and more time on approaches that did eventually pan out, you likely would have arrived at a workable solution sooner.
For example, if the core Go team had spent more time exploring C#-like approaches (as some suggest they should have), it's possible that would have delayed the whole thing.
(btw, consider me a "long time listener, first time caller" -- I pretty much always pause to read your comments while flicking through discussions here, including I appreciate you often bring in historical context, even if I might have a different take, or even if I might have expressed your take differently ;)
Comment by xtracto 2 days ago
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.
Comment by interf4ce 2 days ago
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.
Comment by cyberpunk 2 days ago
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…
Comment by brokencode 2 days ago
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.
Comment by inigyou 2 days ago
There is room for a language without generics. There was a language without generics. Now there is not.
Comment by brokencode 2 days ago
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.
Comment by metaltyphoon 2 days ago
Comment by inigyou 2 days ago
Comment by brokencode 2 days ago
This is just basic programming stuff and not limited to esoteric research languages.
Yet so many in the Go community treated it as some kind of language-ruining complexity.
Comment by pjmlp 2 days ago
The usual RIX approach, followed the whole devops hype cycle that created all those CNCF projects half of which no one in devops space actually knows they exist.
Comment by blandflakes 2 days ago
Comment by pjmlp 1 day ago
Alone the code generation machinery that Kubernetes used to have for working around lack of generics.
Comment by improgrammer007 2 days ago
Comment by wannabe44 3 days ago
Comment by TheDong 2 days ago
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? parsed, err := slices.MapErr(inputstrings, time.ParseDuration)
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.Comment by wlamartin 2 days ago
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.
Comment by ninkendo 2 days ago
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.
Comment by tialaramex 2 days ago
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.
Comment by troupo 2 days ago
> 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.
Comment by tyre 2 days ago
Comprehensions are not a well-designed feature but a consequence of poor design.
Comment by pjmlp 2 days ago
Which by the way has no issues being whitespace sensitive and having multiline lambdas.
The only reason Python doesn't support them is Guido not wanting to have them.
Comment by wannabe44 2 days ago
Comment by bborud 2 days ago
Comment by inigyou 2 days ago
Comment by 9rx 2 days ago
The natural consequence of a "bullshit job" economy.
Comment by troupo 2 days ago
Comment by pjmlp 2 days ago
Comment by inigyou 2 days ago
Comment by pjmlp 2 days ago
No one among the folks that created Scheme or C, would assert R7RS or C23 are that simple.
Comment by pstuart 3 days ago
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).
Comment by cyphar 3 days 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.
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.)
Comment by aatd86 3 days ago
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..?
Comment by cyphar 2 days ago
I was a little surprised how often I ran into this issue when using libraries that started defining structures as generic containers.
Comment by aatd86 2 days ago
Comment by cyphar 1 day ago
But traits wouldn't really make sense for Go -- my point was that there is are solutions for this problem in other languages; Go's original design goals don't gel well with generics which leads to these kinds of issues. They felt generics weren't necessary so they didn't design Go with them in mind.
Comment by aatd86 1 day ago
So even if generics were there from the 'get-go', I am not sure this exact feature would be added anyway. In fact, it is mostly contrary to Go's structural polymorphism, let alone parametric polymorphism.
Typically what you want would be defined as a function, a specific wrapper type... If it is not already a common method of each shape.
So far, I don't feel like the design of Go's generics suffers from any real issue. The implementation is not 100 percent done yet. But the plan looks sound to me.
Comment by pstuart 3 days ago
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.
Comment by cyphar 3 days ago
Comment by pstuart 3 days ago
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).
Comment by wbl 2 days ago
Comment by pstuart 2 days ago
As Rust and its ecosystem mature and LLMs get better and cheaper, I imagine we'll see more ports of Go projects to Rust.
I love Go and admire Rust but I didn't have the energy to get through the learning curve, and Go does enough for me for what I need so I'll be ok if I never properly use or master Rust.
An ex-boss loathed Go and cited "but the nil pointers!" as his reasoning for his contempt and made sure to migrate the flagship project written in Go to Rust. People get emotional over languages but they're just tools and not worth fighting over.
Comment by troupo 2 days ago
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.
Comment by Hendrikto 2 days ago
Just wrap them. The external type is theirs. Make your own wrapper and do whatever you like with it.
People want every language to adapt to them, instead of adapting themselves to the language.
Comment by cyphar 2 days ago
Comment by tikhonj 3 days ago
Go is... very much not on the Pareto frontier of programming language design.
Comment by whateveracct 2 days ago
100%. it pisses me off so much that i have to hear this idea thrown around constantly.
Comment by fragmede 3 days ago
Comment by pstuart 3 days ago
Comment by dgunay 2 days ago
Comment by ncruces 2 days ago
Comment by kamma4434 2 days ago
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)
Comment by pjmlp 2 days ago
Comment by tzone 2 days ago
It is crazy that people still don't understand why JVM sucks, and why GO's approach to minimize GC pause latency is far superior design decision compared to whatever JVM has been trying to do with its GC iterations for god knows how many years with gazzillion different variants that all suck in different ways.
Comment by vips7L 2 days ago
Comment by bheadmaster 1 day ago
Go wins not by technical ingenuity, but by simply not making fundamentally bad decisions in the first place.
Comment by vips7L 1 day ago
Comment by bheadmaster 1 day ago
The very fact that Project Valhalla had to create new opt-in syntax for value classes, in order to get what Go does by default on all its types, is the admission of defeat of a herculean battle of attempting to optimize desite fundamental problems with Java's design.
Value classes won't make criticism outdated, because they don't fix any fundamental problems, they just provide an opt-in feature with significant restrictions.
Comment by vips7L 21 hours ago
If your argument is that optional syntax is bad, sure, we can do that, but it has nothing to do with GC performance.
Comment by bheadmaster 15 hours ago
Go's GC is nowhere near as complex, and yet it performs much better, because Go decided not to shoot itself in the foot.
Comment by vips7L 4 hours ago
Except this criticism will be outdated. (but then you moved the goal post to syntax).
> Go's GC is nowhere near as complex, and yet it performs much better
Benchmarks prove otherwise.
Comment by bheadmaster 2 hours ago
> Benchmarks prove otherwise
Well, good luck running the benchmarks on your production servers.
Comment by kamma4434 1 day ago
Comment by Pooge 2 days ago
Comment by tzone 2 days ago
Design of Go allows programmer to rewrite their program to make it as GC efficient as needed. You can even have essentially zero GC overhead and do stuff manually for really high performance needs. And you can also write regular code when GC isn't a big deal.
Those options simply don't exist in Java language. Your only bet in Java would be to embed some C code which is a nightmare of its own.
There is a reason why almost all backened systems that run on JVM are a huge pain in the ass even at moderate scale. They all end up rewriting parts in other languages and at the end just rewriting the whole thing.
Comment by vips7L 1 day ago
Comment by hashmash 2 days ago
It should also be noted that the low pause Azul C4 collector was available in 2010, but it was a commercial product.
Comment by Pooge 2 days ago
Comment by age123456gpg 2 days ago
Comment by shikck200 2 days ago
Java is the enterprise language that make programming suck.
Comment by sideeffffect 1 day ago
https://marketplace.visualstudio.com/items?itemName=Oracle.o...
Comment by Shish2k 2 days ago
Comment by vips7L 2 days ago
Comment by jiehong 3 days ago
I wish they wouldn’t mix mutation methods in there, but ok.
Comment by improgrammer007 2 days ago
Comment by pstuart 2 days ago
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.
Comment by pjmlp 2 days ago
Comment by pstuart 2 days ago
They didn't know how to do it "correctly" -- it took outside help from Philip Wadler (who helped with Generics in Java) to make it work acceptably.
Of course the core team were a big bunch of dumb old stupidheads and they made a dumb language for dumb people, so you win, you're smarter than them.
Comment by pjmlp 1 day ago
Of course the language is for dumb people, Rob Pike even said it officially, poor souls that apparently cannot master better tools, even though they are able to work around Google's stupid hiring practices.
Comment by pstuart 1 day ago
His comment about making it easy for junior engineers was about pragmatic software engineering and being able to deliver product at scale.
There's plenty of far more advanced programming languages, e.g., Lisp, OCaml, Haskell, and even C++ as far as power of the language. But only one of those has gone mainstream and it was the C++ compilation time that inspired Go and Rust was also trying to be a better way to code within that paradigm.
It's a matter of taste, and while hating being forced to use a language you don't like to use is one thing, hating the language itself is silly -- it's like hating roses because you like orchids.
And if you think the original Go team was composed of dumb people you are also mistaken. They built a language they wanted to use, which I'd posit is a signal of "worthiness". Many people (including myself) enjoy using the language and it's created a lot of value in the software ecosystem.
Comment by 9rx 2 days ago
Especially within a company more powerful than the individuals. The project was done, at least feature-wise, but then Google set it free to become a community-driven project, which opened the freedom to do new things again: https://go.dev/blog/go2-here-we-come
Comment by improgrammer007 2 days ago
Comment by wbl 2 days ago
Comment by pstuart 2 days ago
> 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.
Comment by jppittma 2 days ago
Comment by kokada 2 days ago
Comment by pstuart 1 day ago
Comment by pphysch 2 days ago
Unless they are just here to complain, of course.
Comment by win311fwg 2 days ago
Comment by amazingamazing 2 days ago
Comment by improgrammer007 2 days ago
Comment by ninkendo 2 days ago
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.
Comment by kokada 2 days ago
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.
Comment by amazingamazing 2 days ago
Comment by saturn_vk 2 days ago
Comment by rapnie 2 days ago
Comment by improgrammer007 2 days ago
Comment by everybodyknows 2 days ago
For those who've been following Golang more diligently than myself: Is there a way now to define a slice-based type that enforces strict typing of its index, and yet preserves the compactness of the standard syntax "s[i]"?
Comment by bheadmaster 1 day ago
Comment by dwattttt 1 day ago
For containers, you can do this to make it a type error to index the container with anything other than the newtype you made.
In some languages you can replace/implement the indexing operation a[i] with your new type, so the code looks the same as usual, but gets the benefit of preventing the wrong 'integer' from being used.
Comment by kubanczyk 23 hours ago
Nope, no way to do that.
I wonder about ergonomics. I can imagine "academic" usage, but I shudder at the perspective of the impact on a typical CRUD app.
Comment by athorax 3 days ago
Comment by nasretdinov 3 days ago
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.
Comment by TheDong 2 days ago
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.
Comment by rattlesnakedave 2 days ago
Comment by overfeed 2 days ago
Comment by bananamogul 3 days ago
"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.
Comment by orf 3 days ago
Comment by alfiedotwtf 2 days ago
Comment by throwgolang 1 day ago
Comment by valcron1000 2 days ago
Comment by shevy-java 2 days ago
Comment by quchen 2 days ago
Comment by znpy 3 days ago
Comment by throw_m239339 2 days ago
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...
Comment by znpy 2 days ago
Comment by pjmlp 2 days ago
Go EE is the plethora of CNCF projects.
Comment by znpy 1 day ago
Essentially an abstraction layer above the infrastructure (physical or virtual).
Considering it was released by google AFTER the launch of google cloud platform, i honestly see it as a way to make apps more easily portable off AWS.
Comment by roundwego 2 days ago
Comment by akiarie 2 days ago
Comment by woodruffw 2 days ago
(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.)
Comment by troupo 2 days ago
Working on any collection is easier with generics. Working with anything that accesses data in uniform way (APIs, SQL etc.) is easier with generics.
Comment by jerf 2 days ago
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.
Comment by pphysch 2 days ago
Comment by Quitschquat 2 days ago
Comment by pif 2 days ago
Comment by win311fwg 2 days ago
Ironically, only poor developers have the luxury of being able to choose something like Haskell as they never have to worry about building something anyone else wants to use.
Comment by foldr 2 days ago