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.
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.
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.
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
People always forget this other post when arguing for the home team.
"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."
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 ;)
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.
Usually, if you're in microservices land, it's often possible to spin off a separate service and move the part that has different requirements over. Obviously that isn't always trivial, but service boundaries at least give you that option.
I had a team convinced that a particular subsystem was a mess and that they needed a stronger type system to build it properly because Go would just lead to hacks. They built a better one in Rust, deployed it, and moved traffic over.
Ditto, to some extent, if you're building desktop applications. Many large desktop applications already have multiple components or processes under the hood, and if you're at millions of lines, introducing that complexity may be worth it.
However, not all programs fit the above category. Sometimes you really do need a single lightweight binary and it needs to do a lot of stuff. But I think it's relatively uncommon to need one single binary that is simultaneously very large, architecturally diverse, and unable to delegate substantial complexity to other processes or services.
I see this most in the Kubernetes world, where something that starts as a simple binary gradually increases in scope and complexity. I've seen vendors struggle to keep up and start playing whack-a-mole with that complexity, but generally Go still scales surprisingly well here. Outside of K8s, there are also domains where you may strongly prefer a single-process or single-binary application, such as edge computing or developer tools.
Luckily, products where Go excels in these spaces also tend to have a natural upper bound on their domain complexity, because the operating model is generally about doing one or a few things well.
If Go's type system actually starts fighting you rather than merely being an inconvenience, then yeah, I would seriously look into moving off Go.
If you're working in a space that doesn't fit any of the above, I'd genuinely be interested in hearing about what you're building.
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…
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.
Go only took off thanks to Docker pivot from Python into Go, and Kubernetes from Java into Go, after the respective teams got some Go folks into the project.
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.
Oh, is that how it works? So, when somebody says a model is running at X tokens per second, it means that the thinking process is running at that, and output tokens are much lower then? Thanks to the explanation.
It’s all just tokens to the model. Whether it’s thinking tokens or output tokens, they take the same amount of computation to produce. The only difference is whether the token is displayed to the user.
So your complaint is about LLM slop, but can’t point to anything that’s actually wrong about it other than that there are dashes?
You can use an LLM to create work that isn’t slop. And you can hand write slop with no computer involvement at all. Most of the people I knew in high school 15 years ago would write slop on a daily basis.
Personally, I often paste screenshots into Claude Code of the application it’s working on. And I’ve even had it work autonomously on something and regularly grab its own screenshots.
Or sometimes I will have tables, charts, or even screenshots of text that I would otherwise have to have another step to OCR or type out.
Multimodal saves me time on a regular basis. Not sure it’s a game changer, but just lets me communicate with the model in all sorts of ways that would be harder otherwise.
Wait, you think not giving additional aid = responsibility for whatever happens in the developing country? Does this blame go for the rest of the year, decade, or century?
Does giving aid in the first place automatically trigger this? If I gave $500 to kids cancer research every year for 5 years, and then I don't give this year, do I have blood on my hands every time a kid dies of cancer from now on? And if you didn't ever donate, you don't?
> If I gave $500 to kids cancer research every year for 5 years, and then I don't give this year, do I have blood on my hands every time a kid dies of cancer from now on? And if you didn't ever donate, you don't?
> How does this work?
Okay, since the topic at hand is steelmanning, that is, replying to the strongest possible argument, let's practice that.
I invite you to watch this video, which is a short lecture that indeed exposes the strongest argument for this exact proposition.
There were a lot of voters who didn't want those cuts. So they complain. Is that what swing voters thought they were getting?
The government spends like 1000 billion on the military, a couple/few 10s of billions on aid is just being charitable. And projects soft power, buying good will. And was probably well used by the cia.
And then there's the philopher Peter Singer, who would say that not helping other people is immoral. Most people wouldn't go that far, but some do. Some religions ephasize such things.
> Wait, you think not giving additional aid = responsibility for whatever happens in the developing country?
This, and your $500 cancer donation, is an absurdist reduction of the problem.
The USAID contributions weren’t anything like your $500 example. It was the entire infrastructure for medical care and immunizations that people relied on.
The proper way to wind these programs down, if it was appropriate, was to give an off ramp so their governments and other organizations could minimize a plan to fill the void by a certain date.
If you take responsibility for something medical on a large scale, doing a sudden rug pull has predictable consequences. Those predictable consequences cannot be separated from the person who made the decision.
I think these terrible analogies about donating $500 indicate that you don’t understand the problem.
I don’t know, does global cancer research shut down when you stop giving the $500? Do kids immediately stop receiving treatment?
USAID literally ran ambulance systems that shut down due to lack of diesel. They delivered lifesaving drugs that stopped.
We made commitments to communities to run these services, then suddenly killed them off. We didn’t try to find other countries to step in. We didn’t try to get the local governments to take over.
We did jack shit to try to preserve lives in this transition process.
But if we never had, those deaths would have been happening that whole time, wouldn't they?
Unless you're saying that the aid being there in the first place was actively discouraging any self-sufficiency by the sovereign governments who are actually responsible for those areas... in which case I can see why some people (both in the West and the developing world) oppose foreign aid in the first place. Even if we passed the baton around to other countries, the band-aid will still have to be ripped off at some point.
I'm actually not anti-foreign-aid entirely, I think in addition to the humanitarian benefits which I do value, when done well it helps project soft power, giving normal people a concrete counterpoint when extremists like Islamists try to claim "America is an evil empire that hates you and wants you to die." So in that way I don't endorse eliminating USAID... I admit that mainly it was a circus trick to try to hand supporters a concrete W for the vibe of "America First" and/or "Saving Money"
Maybe the deaths would have happened, or maybe these places would have become self sufficient or received aid elsewhere. There’s really no way to know.
But we committed to providing this aid. Even if we think it would be better in the long term for others to do it instead, there’s no excuse to cut it off suddenly without making reasonable efforts at a transition.
Imagine you’re the long term caretaker for a family member, then just decide one day you’re tired of it, so stop. You make no attempt to find somebody else to take over, you simply stop bringing them their meds/food or whatever.
Do you think your family should then forgive you after grandma dies? I don’t think so.
You make a pretty fair point there. It would have been better to announce a timeline even if it's like a year. If nobody wants else to help, at least we would have forced our allies and enemies to all go on the record that they won't.
The people at risk witout the aid, though, are more comparable to a drug-addicted teenager who has criminally negligent parents than a truly-disabled grandma. Hopefully we can save a little bit of the blame for those local kleptocrats, who have been absorbing aid from all available sources and failing to properly govern since the creation of most of these countries. (Yes, I agree with you that our current iteration of government are mostly kleptocrats themselves).
I don't think that one USAID tracker website is very reliable, sadly. I also believed this. A bunch of programs were rolled into the state department, and for instance mortality stats for South Africa are significantly upwards-diverging from their model. Now SA is an unusually well-put-together African state, but the study could have modeled this and didn't, so I don't think it should be taken as gospel until more countries report in.
Elon himself promoted Grok’s “spicy mode” that allowed generating NSFW content that the other AI vendors wouldn’t touch with a 20 foot pole.
Believe whatever you want. Elon’s beliefs and personality problems have been baked into the core of Grok, so it’s no surprise that it turned out to be a CSAM-generating MechaHitler that steals people’s data.
Anybody surprised when Grok turns out to be trash really should read up on the guy who made it.
Yet we (rightly) condemned those that used this leniency to do nefarious things.
I'm really ready to get on the Elon hate train, and I will grant you that there was a problem that needs fixing, but I'm really not happy with the amount of censorship on these generative AI platforms.
idk how to interpret all this, despite being genuinely anti-Elon, I don't think I'm personally willing to immolate a company forever because the guardrails were temporarily too loose.
I'm not trying to make an equivalency for facts vs deepfake porn, but there is one there unfortunately, and overall internet freedom has been curtailed a lot by advertising friendliness.
To be clear, I am not agreeing that people only upvote their favorite billionaires, just that if this particular thing was done by a Chinese model it would not have gotten the same attention
> Forecasting models predicted that the current steep funding cuts could result in more than 14 051 750 (uncertainty interval 8 475 990–19 662 191) additional all-age deaths, including 4 537 157 (3 124 796–5 910 791) in children younger than age 5 years, by 2030.
So you think a malaria prevention program can be canceled without notice and no one will die from that?
The only people who believe that do not believe in anything. They think there is no such thing as competence or honesty because they have never experienced it.
We were responsible for providing notice and a transition period when we stopped paying for it. We didn't have to pay for it, and they didn't have to die. But we have an administration that can't plan a pool party.
Should I pick a model
a) run by a lying crypto bro once obsessed with scanning eyeballs
b) that costs too much and resulted bombing innocent kids
c) that is cheap but ultimately owned by re-education camp operators
d) something else
> Then the author just kind of disappeared and the project stalled.
There was more to the story than that. They made some major breaking changes in v0.19 that broke a lot of apps and left no path for them to continue with Elm, then dug their heels in when the community protested.
If you had an app at your company that used the features they decided not to allow any more, you either had to start deciding which fork to follow or start planning to rewrite your app in something else.
That evangelism turned into an uncomfortable gaslighting where half of the community was trying to tell you that this change was what was best for the language and that you didn’t really need that feature anyway.
There were several forks but I don’t know if any got traction. It felt like an already small community was fracturing into even smaller communities right after alienating a lot of people.
It's vibe-coded (hmmm), Elm syntax, server-side, SSE, compiles to Go, Go FFI, TEA, web/cli/TUI/desktop targets, consistent Db, Ui - a lot of things to like if it all comes together.
On top of the already-mentioned JS interop breakage, Elm 0.19 also dropped native Websocket support[0]. The API had issues (fair), so it was dropped rather than improving it due to wanting to do it perfectly (okay, I guess), buuut due to the JS interop restrictions this meant that 3rd-party experiments or alternatives were impossible (??), which meant that any use of Websockets was in practice now completely impossible! If I recall correctly something similar happened to other core libraries.
This "nobody is allowed to do this until Evan himself has made time to come up with a blessed solution" style of development left a lot of people quite disappointed. Elm was marketed quite heavily as the best thing since sliced bread and the future of front-end web development, but in reality it turned out to be just Evan's toy language which you could look at but weren't allowed to touch. Which is of course allowed, but it does rapidly kill any kind of community around it.
Synchronous interop was removed from Elm. That sucks for synchronous stuff and anything too trivial to be worth async interop.
But async interop is still available. Anything networked, like websockets, is a natural fit for async interop. i.e. a Send(Req) | Recv(Res) port.
It's fine to be mad that a "BDFL" decided on a different set of trade-offs than your preference, but that's what happened.
It's also a learning lesson for people who thought that a tiny, pre-v1.0 ecosystem that already had breaking changes would never break again especially in a way they disagree with. I think it's time to just accept the lesson.
> It's also a learning lesson for people who thought that a tiny, pre-v1.0 ecosystem that already had breaking changes would never break again especially in a way they disagree with. I think it's time to just accept the lesson.
This gives flashbacks of the last time I discussed this 7 years ago: Even trying to bring it up would bring denial that it was a problem. It was your fault for using it wrong. If you could demonstrate the cases where it continued to be a problem, it was still your fault for using the project.
Even the pre-1.0 projects I use that have breaking changes will announce a transition period and gradually deprecate APIs over several releases. Community feedback is monitored and the deprecated API may be kept longer than originally planned until suitable alternatives can be produced. Elm wouldn't even consider any of these.
The direction of the argument also changes based on the situation. When Elm was dropping breaking changes in 0.19 the story was that it's a fast changing pre-1.0 project and it was our fault for not expecting breaking changes.
Then they went 7 years without a release and the argument became that Elm was so stable that it was our fault for expecting updates to a mature and stable project.
I'm not mad, I'm disappointed. Elm was quite promising prior to this, but 0.19 essentially killed it.
And the problem isn't just that sync interop was removed. That would've been fine. It's the double-whammy of 1) killing sync interop, 2) making async interop libs impossible, 3) still allowing it for "blessed" libraries, and 4) gaslighting everyone else that they were Holding It Wrong.
Breakage is totally fine, I never expected anything different from Elm. But community-killing permanent core feature removal is a bit much, is it not?
I luckily never invested too deeply into the ecosystem so there wasn't a lot dor me to "learn", but it sure ruined any chances of me - and with me I bet a lot of other people - ever looking at an Elm 1.0 or Elm++, and considering the valuable insights gained from TEA that really is a shame.
I think you are taking the wrong lesson. The lesson is not "things make break." Many languages break things. Haskell has made it a feature, not a bug.
But this change did more than break things. It meant people using Elm in production had to abandon it, nearly immediately, as all future work would first require them to port the whole stack, in one fell swoop, to Elm (and this was before tail recursion modulo cons was implemented, but recursion was forced).
Imagine if, in Rust's infancy, it decided to remove the C FFI with the argument that people should instead, naturally, rewrite that code in Rust. What would have happened? People would have abandoned it in droves, and it would have been essentially relegated to a research language, never again suited for prime time.
Elm was better without custom kernel modules and sync JS-interop.
It kept the Elm kernel small and portable. It forced the 3rd party package eco system to innovate and create things rather than just wrap existing Javscript libraries.
It has enable me to port the small kernel now to C++ for an Elm to native compiler.
Also, if you really wanted to bypass it and have your own kernel, that was always possible and not hard to do. Even in 0.18 custom effects modules could not be shared on the official package site.
> Elm was better without custom kernel modules and sync JS-interop.
That may be true, but what would have been even better is never having the feature, as opposed to adding it, allowing many people to become dependent on it, and then taking it away. (Like everyone else, I'm not claiming Evan should not be able to make such changes -- just that making such changes will breed predictable resentment.)
> It kept the Elm kernel small and portable.
This is a good thing.
> It forced the 3rd party package eco system to innovate and create things rather than just wrap existing Javscript libraries.
This is a bad thing, presented as a good thing. Boring as it may be, if wrapping an existing piece of working code does the job with no downsides then it's always better to do that than reimplement it for reimplementation's sake.
Custom kernel never was a feature the language had. It was an implementation detail that people discovered they could hack, but it was never meant as a feature.
> It forced the 3rd party package eco system to innovate and create things rather than just wrap existing Javscript libraries.
Writing everything yourself is not innovation. It's busy work.
Sure it's a good way to learn a language, but when you just want to build an app why would you build yet another searchable select when there are more than enough JS options already available....
IMHO this busywork is (mostly) preferrable, because it meant that any package was truly pure Elm, and you could blindly trust it not to be some wrapper that crashed at runtime.
It's a tradeoff, sure. But this way, Elm didn't become just another "JavaScript with better syntax".
Lots of things were disallowed in 0.19, but probably the most disruptive were that custom native modules were disallowed (which basically means that, only certain official packages would now be allowed to directly call native JavaScript), and the package manager was locked down (which means that you can only install packages from the official elm repository, not GitHub or anywhere else).
You can still indirectly call native JavaScript, in a message-passing kind of way (via Ports or custom elements) but these changes were still really disruptive to many codebases.
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.