If I'm allowed to make a critique, I'd say that the module doesn't look very much like Go. The (ab)use of generics and having every policy to its own package feel a little over-engineered.
I might be slightly biased, because, not knowing about this project, I implemented half of the functionality in my own simpler module: https://git.sr.ht/~mariusor/ssm
Author here - I do get this critique, and of course API design is subjective, but there was a rationale behind the design choices. Putting each policy in its own package allowed the API to be slightly more concise. And having the API use a generic to represent a function's result type allowed different parts of the API to be independent and composable while still ensuring that result types align. A bit more on that here:
> allowed different parts of the API to be independent and composable while still ensuring that result types align
I found that states are not really required to know anything about internals of the executed code. All that a policy needs to know if the next state is an error or not, that's all.
I might have simplified the problem too much for my library to be useful in real life applications, but I still hold hope that a
stateFn func(context.Context) stateFn
is expressive enough for implementing the whole functionality.
If I may make another suggestion, I found that for more complicated flows, it is useful to be able to build a graph. I have made a rough approximation of a toold for my project based on parsing the go sources and inferring a state diagram from it. You can check it here: https://git.sr.ht/~mariusor/ssm/tree/master/item/cmd (Apologies for the lack of documentation, my work is much more recent than yours :D)
circuit breaks and a end point health dashboards are sure ways to be promoted above ic5 in most dysfunctionaly big companies.
I've spend a lot of time ripping them out of java projects abusing those patterns for internal services, where service interruption was caused more by the delays from the circuit breakers than actual problems with the internal end points. but everyone who added them got promoted in the past.
and on those companies, most java people are now go devs when they were retrained during the move to the clouds because then most tools were go or python.
Very popular .NET library that does this is https://github.com/App-vNext/Polly. It seems failsafe borrows heavily from it given first Polly version was released more than 10 years ago.
Worth noting as well that in more recent .net versions Polly has been brought into Microsoft under (if I remember right) microsoft.extensions.http.resilience
Microsoft.Extensions.Resilience and its downstream dependencies simply reference Polly, it continues to be an independent and actively maintained set of packages.
Author here - yes, Failsafe-go definitely took some inspiration from Polly, but it also borrows ideas from the Failsafe JVM library [1]. Polly and Failsafe influenced each other back and forth over the years.
Depending on your performance requirements extra milliseconds of roundtrip to the externalized queue may be too long of a wait. Or you may simply not care enough for an extra piece of infrastructure that you'd need to support, and maybe this is good enough.
It's is all about trade offs, no solution fits all use cases.
its always about tradeoffs. one nice compromise position here is that the log is always consistent, so you don't need to wait at all if you don't mind losing the tail on a crash.
There's this feeling of fulfillment when what you pray for comes true i just want to appreciate this great spy agent (arpanethack @gmail dot com)at first, his first job for me wasn't successful and i got refunded back but,i had to give him benefit of the doubt to track my husband's phone and it was a success and all information was wired to my phone
I just finished this. To each their own of course, but I found the writing too padded and tonally off-putting at times. Some of the stories felt dated both from a technological stance and a cultural stance. I prefer Azure's Cloud Pattern docs myself (though "Release It!" was really good if you prefer a storytelling approach):
This looks incredibly comprehensive, thanks for sharing!
Should have added that I read this book in 2016, and the first edition is even older, so there’s naturally been lots of new (and exciting) developments in this area!
I could be wrong but I don’t think OTP natively implements a load shedding strategy or concurrency controls or request hedging. You would implement a supervisor to do so if you don’t want to overload an external system like a DB or microservice (e.g https://github.com/fishcakez/sbroker).
“these libraries are just what otp does natively”.
“Here are some things in this library that otp doesn’t do natively.”
“You could build a erlang library for that”
Having enthusiasm for erlang/otp is fine. It’s a pretty great stack. But there are lots of reasonable reasons not to use it and lots of distributed systems knowledge we’ve picked up since it was designed. Some of which are encoded in libraries like these.
Imitation is the sincerest form of flattery that mediocrity can pay to greatness...
Im guessing that pretty much sums up how you feel. "Why isn't my tool more popular" is really the subtext of your question here.
A lot of GO devs came from java,ruby,python,php ... For 3 out of 4 of those static typing is an upgrade. For all of them compile to executable is a massive change and a good one. We're not moving code and runtime to a box to execute...
Its a breath of fresh air that if I need a tool I can quickly write it in go, compile it, throw it on to the server its needed on and be done with it. I dont have to instal vm/runtime/server to make that code work.
Dont think that matters? I have a Rpi camera server that is just a go binary running... All I did was wget it and turn it into a service.
Furthermore every one who is coming from those languages has seen upgrades go all sorts of directions.. php 4-5-6-7 were somewhat smooth but evolved the language, there is a way. Python 2 v 3 you can evolve a language and there is a BAD way. Python venvs are a pain... and ruby gems have their issues... Go's no breaking promise and packaging choices speaks to all the things that these devs lived through.
And the laments of "go lacks" or "too much boiler plate" are features not defects. Write dead simple, easy to read code. Deal with your errors on the spot in a pattern that everyone will recognize. Im not sure that I qualify Erlang/Elixir easy to read or "simple" (effective I will give it).
So yes, people are stealing OTP... take the compliment and go out and invent something else cool that we can "borrow".
I write a lot of GO? Do you think I'm unaware of its warts? Do you think I'm not sick of typing err != nil? Do you think I dont sometimes cut corners and NOT deal with that error on the spot or just _ it cause it's a throw away chunk of code?
Pick a language, any one, and there's probably a post by someone complaining about a bad design decision or a wart on its fuzzy ass... Nothing is perfect.
Yes go has flaws... I find that first class, high speed, testing makes the fact that I screw these things up all the time moot. Test, fix move on to the next project.
And it's why people prefer damn near anything over Erlang. I have been knee deep in RabbitMQ code trying to solve a massive performance issue in a production system and all I was able to learn from that experience is that I hate Erlang. Trying to learn something in anger isn't a great way to do things but using a language that looks nothing like most languages in common use is a recipe for niche status and a reason for people who need the things they run to be understandable to choose a different tool.
As someone who adhores OTP in the abstract but uses mostly Go, I can tell you why I don't use it.
Static typing: I've worked for years with dynamically typed languages and won't go back. Dynamic typing was a big mistake that have wasted years of developer time. Dialyzer isn't an option.
Syntax: Erlang's syntax is not great. Enough said. I think Elixir isn't better, or at least doesn't offer enough of a value proposition over straight Erlang.
Native compilation: Erlang works well for control systems. It's absolutely subpar for anything requiring compute performance or I/O. This means you may find yourself in a sticky position if your backend app has hotspots needing very low-latency, high-performant logic. You may have to write bits in C or some other natively compiled language.
Friction: Erlang is hardly used anywhere. It is sufficiently weird (functional, Prolog syntax, immutable, the process model, antique tooling, not to mention OTP itself) that it's not just something most developers are going to pick up in a day. Forcing Erlang on a team not consisting of a monoculture of Erlang developers is a huge ask in any company. It's hard to hire for. You can't just reassign a random "full stack" developer within a company to a team that uses Erlang, if all they know is JavaScript/TypeScript or Go for that matter.
I think fans of Erlang have to realize that no matter how much they post comments like yours, it's just not going to take off. Sometimes superior tech just doesn't pan out (Amiga, BeOS, QNX), but we move on.
I am optimistic about Gleam, though. A much better language that I would actually want to use, and there's interop with Erlang/OTP, which is a great way to bootstrap a language.
> Native compilation: Erlang works well for control systems. It's absolutely subpar for anything requiring compute performance or I/O. This means you may find yourself in a sticky position if your backend app has hotspots needing very low-latency, high-performant logic. You may have to write bits in C or some other natively compiled language.
This is mostly accurate, but I think Erlang is a good fit for network I/O, especially network I/O with lots of concurrent clients.
> Friction: Erlang is hardly used anywhere. It is sufficiently weird (functional, Prolog syntax, immutable, the process model, antique tooling, not to mention OTP itself) that it's not just something most developers are going to pick up in a day.
At my Erlang job, almost nobody we hired had Erlang experience. It certainly took longer than a day, but IIRC, my first big Erlang server stuff deployed (and mostly worked) a month in, and I was focusing on a PHP service. I probably did some minor stuff earlier, but I no longer have changelogs to reference.
Yeah, some of the OTP philosophy takes a while to understand. As for antique tooling, we just used Make, and that's what I continue to use for my Erlang projects, and well Make is Make... once you learn it, it works. Figuring out how to get dist running and pop debug shells when and where you need them is work, but if you're dropped into a place that already has that, you don't need to figure it out yourself, either.
It doesn’t even necessarily say that. People may reasonably evaluate OTP/Erlang/Elixir[/other BEAM languages] and find them appealing, but elect to use another stack because it appeals more for reasons beyond OTP/etc’s appeal. I made that choice once, but I wouldn’t hesitate to consider OTP/etc again the next time I’m working on a problem they solve well.
Thanks for proving the whole point of this thread (No snark meant). People are indeed recreating parts of OTP in other languages because there are all kinds of reasons to pick a different language than Erlang/Elixer and still want some of the advantages of those languages
I've run high scale, high availability Go clusters involving thousands of nodes, dozens of eng teams, and it leveraged k8s. I've also worked at an Elixer shop with a few dozen engineers at a low scale ecommerce solution. The Go system was doing around 100k api calls a second, the Elixir system peaked at about 3k rps.
I would choose Go every single time. It has a better story for teams working together, largely due to better typing. Lacking types (and specs don't cut it), I still found I had to go up multiple function calls to understand what the parameters were, how they could be used, etc. so much state has to stay in your head, which I did not expect from a functional language
I do, at somewhat medium scale. My experience was similar to other posters here, where we ran into production issues that were incredibly difficult to pin down.
If you'd like, you can attribute that to lack of Erlang experience or skill on my part. But in practice, we instead replaced that system with Go running on a Kube cluster and it's now been rock solid.
I've always heard a lot of good things about elixir - I feel like if it was statically typed, the landscape for network oriented programming langauges would be a lot different. And with how popular microservices have become, how big and complex a lot of cloud architecture has become, that could've been a big difference.
To some extent golang fills that void because of net, it's application in projects liks k8s, and it's concurrency dx through goroutines. But it's also had problems that I don't want to dive into here that I think make it more divisive than would be expected.
And that's my point, a lot of these things like microservices, "severless" and k8s are just poor and overly complex imitations of OTP. Many of these problems have already been solved by this humble stack which has been around since the 1980s.
The ecosystem around Elixir/Erlang is just not comparable to what Python/Ruby/Go/Java/.NET/Node even PHP have.
Elixir is promoted by enthusiasts a lot but once you start writing a project in it, you’ll realize after a couple of months you are writing a lot of things yourself, popular tools don’t have bindigs, lot of libraries that do exist are abandoned etc.
I worked on a Phoenix project that had a Graphql API, we couldn’t integrate it with Apollo Graphql because the Elixir library didn’t support some features, had them in the backlog for years. The company had 20+ other services in different languages, none had this issue.
Perhaps you’ve heard of Discord, Pinterest, WhatsApp, Spotify, Moz, PepsiCo? I’m sure I’m missing a whole bunch of names as I’m in the bootstrapped world where you’re seeing a lot of Elixir (especially now that LiveView is stable).
For one, Phoenix is a great web framework (especially with LiveView) and Ecto data layer. Show me any tooling in Rust or Go that is as productive for full stack.
Phoenix and especially Ecto are fantastic, and liveview is really cool. Having worked in a couple Go organizations and only one Elixir one, I don't agree that this is what makes one productive. These tools save some time. What really saves time is being able to jump into unfamiliar code and be productive. Go is best in class for this after the team can no longer keep the entire code base in their head.
I’m sure Phoenix is great, but in real-world experience, my former startup tried to use it, found the ecosystem and tooling extremely lacking, encountered severe problems with the learning curve, and pivoted to Rails.
Strange. I've dropped into several different startups and gotten people up to speed in days if they were experienced devs. One case was more painful, but much of that was due to engineering being outsourced to a team of contractors and management being from a non-tech industry.
Usually the hardest thing for devs I've seen learning Elixir is just getting used to immutability. What were the severe problems with the learning curve your former startup encountered?
C# with ASP.NET Core + EF Core, AvaloniaUI or Uno or GTK bindings if that's your preference, Unity or Godot, and more.
Can build for Linux, macOS, Windows, Android and iOS, either JIT (better for servers, sometimes GUI/Games) or AOT (better for phones, CLI, serverless).
Its build system and CLI tooling is very productive and easy to get started with.
Reflection is notoriously problematic in regards to type safety in many languages and often isn't the right tool for the job, including C#.
The base64, in most languages, requires importing the right package or namespace.
Converting bytes to Base64 is just Convert.ToBase64String/CharArray(bytes).
I can't believe someone would seriously and unironically suggest PHP. It does not even compete at the only area it's applicable at being back-end or server-side rendering.
> It does not even compete at the only area it's applicable at being back-end or server-side rendering.
I'm not sure what you mean here. I've deleted my blog, but I once published benchmarks showing PHP was faster than C# in some cases. I'm currently working on a reimplementation of some C# stuff in PHP and competing with C# quite well.
PHP is an interpreted dynamically typed language. It would always be slower and would always have strict performance ceiling. Given sufficiently competent compilers of similar level of engineering effort, that would always be true. PHP is within the same performance weight category as JavaScript. C# is in the same weight category as Kotlin ad Java, sometimes Rust and C++. It is guaranteed in the same category as Rust and C++ for performance-oriented code (struct generics and spans).
To think that an interpreter, where each single instruction is, in the very best case, a computed goto jump with args evaluation, would execute the code faster or comparable to code that is compiled to CPU instructions, is insanity. Even when/if PHP gets JIT compiled, it still has the same language constraints where even something as sophisticated as V8 has to contend with language spec so it cannot optimize away e.g. property access.
It might be best to re-evaluate prior assumptions and knowledge which do not correspond to reality.
> I suppose mental afflictions come in all shapes and forms.
Yes, I suppose some people need proof of things; especially things that aren't that easy to google because it's new and the language has been around since the web's infancy.
I've worked in companies that used Java and companies that used Elixir. No (noticable) difference in availability, performance, bug count or business success (perhaps the Java ones were more successful).
We need to drop this fight around tools. Discussions are fine, but "My tool is better than your tool" doesn't get us anywhere (not saying you're doing this).
In general there is not a lot of innovation (in the mainstream), the only recent thing that comes to mind is Rusts borrow checker.
A company I was working for was acquired by a larger company that was a Java stack. I reimplemented our entire Rails app in Elixir into their stack in about a year. Eventually they decided to port it to Java. Took 2 seniors and a junior a year and it still doesn't perform as well as the Elixir app (soft realtime websocket stuff).
It's been painful to see how much Java has squandered it's potential. Putting aside something like OTP, just the concurrency in Java has been confusing - project loom has been on the horizon for a long time, and it makes it difficult to invest in something reactive like Reactor/Webflux. Or, having quickly reviewed elixir, since it uses the actor model, something like Akka - which words don't even describe how much of a dumpster fire that whole thing became (which I feel like I dodged a bullet because my old boss was super gung-ho about how Akka was the next big thing a few years ago). And if we're talking about things on a platform level, then... spring is not it. Others have mentioned K8s, what takes a simple yaml config there takes like huge guides, a wing and a prayer on Baeldung articles, and a soul crushing soup of XML for config (yea I know Spring supports other formats, but it's roots are in XML and old guards just copy that stuff over).
It doesn't seem like Erlang/Elixir community have these problems. Idk if it's because I am not invested in it so the grass looks greener on the other side, but it really does seem like they've got a productive and focused solution.
You might be right, I don't know. I got off the Java train some years ago, sadly by moving to Scala (what a mistake!) - so I have no clue about the things you are talking about. As the companies I worked for were making hundreds of millions of yearly revenue and lots of profit based on Java, not sure they needed that.
I only have some experience with Scala/Akka. Not sure if Akka was bad or it's the actor model, I found it hard to debug and understand code written by others.
I'm now 50+ and I use Go. It's so simple it even works for old people.
There is a delightful new language if you are really insistent on having static typing while using the Erlang runtime: https://gleam.run/
Though I am not buying the idea that lack of static typing would hinder adoption as many of the most popular backed languages are traditionally dynamic with gradual typing only becoming popular in recent years.
I see comments like these all the time Haskell/Erlang/Elxir/Elm/Ocaml are the greatest thing ever. However basically no one uses them for work. There was a post on HN about how OP has given up on using Haskell in production after 10 years of trying to do so because its just not feasible. (I couldn't find the story on a quick search.) But they still use it in all personal projects. They appear every now and then and have scared me away from non popular languages for the most part.
I just see too many comments on HN from people that have decades more experience than me that say that the theory of these amazing languages simply does not survive contact with the real world most of the time. I'm sure there are great things built with them somewhere out in the world. I just don't think its worth it for the vast majority of developers to worry about.
I don't know if I have decades more experience than you but I can confirm that the amazing language Haskell survives contact with the real world. Here's what I made with it: https://groq.com
On that, and slightly further off topic, if there’s a tutorial, cookbook, or worked example of using OTP but written in Gleam, I would lap it up. Enjoyed learning the language but kinda got stuck there. I didn’t want to have learn Elixir or Erlang just for that.
Is it in terms of hiring? Try posting a fake but real sounding job in the Elixir Slack group. You will get a ton of interest from people dying to use Elixir professionally.
Yeah anecdotally I see tons of developers who are looking for Elixir work. How can a language simultaneously be hard to hire for _and_ hard to get hired in?
Nullable types allow devs to write safe functions (functions that don't allow nullable types as input) and spread that safety through the whole codebase. That safety isn't even an option without having nullable types. Nullable types also mean the compiler knows and warns devs if they forgot to check for nil. There's really no reason why Go sholdn't have nullable types.
“Nullable types” is a phenomenon that occurs when non-nullable types are introduced to a language that started with null and already has libraries that rely on it. When you change the default to be non-nullable, you have to introduce the explicit concept of nullability for backwards compatibility, thus “nullable types”.
Like, if you already had type X that could be null, and you change the language so X can’t be null, now you need the type Nullable<X> (often written something like X?) to let it be null again.
C# and Swift are examples of this evolutionary process.
Sure, pointers are nullable types, but they also are necessary for mutability, so you're forced to use them for non-nullable things too.
You'll notice the go stdlib uses pointers for things which absolutely should not be nullable types, like '(*big.Int).Add(*big.Int, *big.Int)', or 'time.NewTimer() *time.Timer'.
'time.NewTimer' will never return a nil. 'big.Int.Add(x, y)' requires all of those things to be non-nil.
However, those are all pointers for unrelated reasons, like mutability, and so clearly pointers are not _just_ "nullable types".
Agree with this. While I too wish that Go had better ways of handling this, there are patterns and guidelines to follow that should prevent this in practice.
- Use factory functions for struct initialization where members are pointers.
- Use errcheck and friends to ensure errors are not ignored.
- Never dereference the left hand return value if the right hand is an error.
This is why one of the often quoted benefits of golang being it's small languag design is so annoying - it doesn't help if there's then a bunch of other implicit rules that have to be remembered that aren't made explicit through the langauge. It's actually much worse, because these lessons then have to be learned in some other long-winded fashion like official blog posts, additional guidelines, or even worse, random posts online and self-learning through mistakes.
I can't think of a language that has less of these sorts of things you have to internalize than Go. Sure, they're annoying, but so many other languages have even more rough edges.
Maybe you could argue Rust does because it refuses to compile a ton of invalid cases, but I consider the time it takes me to write usable code (whether that's compiling or passing some runtime tests) an important metric.
I believe the problem is if this happens to be an issue in a dependency, which you can have little recourse to. Let’s wait another decade or so and maybe the Go authors will catch on lessons learned by C# (and similar languages) long ago by introducing nullable reference types and all kinds of static analysis to help you not shoot yourself in the foot.
> - Use errcheck and friends to ensure errors are not ignored. - Never dereference the left hand return value if the right hand is an error.
This itself is a very concise argument for exceptions. Automatically checks errors are not ignored, and don't allow access to the left hand return value.
Making bubbling up even easier because people find this too verbose is the original mistake:
if err != nil {
return err
}
I find it verbose too. But if the alternative is to design something that allows you to ignore it 99%, and then put a huge syntactic penalty into doing anything other than bubbling up, then people will just bubble up 99.95% of the time, and complain about the other 0.05.
I'm all for ensuring errors are not silently ignored. That's Go's mistake. Exceptions are not the answer.
I strongly agree. In principle, exceptions are a streamlined version of tossing err up the stack without any embellishment. In practice, I put cookies away in the cabinet so that I don't eat them. Humans write code, and our behaviors change in response to our surroundings. I think a lot more about failure cases in Go than Ruby.
Maybe you're not like that, but you're the exception that proves the rule!
> In principle, exceptions are a streamlined version of tossing err up the stack without any embellishment.
This is exactly what I meant. It's "handling", when viewing the consuming function as failing system. It's not "handling" when viewing the application as failing system.
Yes. The point is the extra steps mean that the delta between “just bubbling up” and “doing something about it” is smaller, which increases the chance that you'll consider “doing something about it.” If “bubbling up” is infinitely easier than handling it, you'll postpone handling it as much as you can.
Your lengthy post just argues you shouldn't “mask” errors in random places. Well that's great that no one said you should. Error handling is not masking, or swallowing errors.
Yet most Go code in the wild does nothing but bubble up the error. Three out of every four lines of code dedicated to doing nothing more than that. If only there were syntax sugar doing so automatically, maybe even a compiler mechanism for forcing you to declare those that you do push up to the caller in such fashion...
I don't even particularly like exceptions either, I just like having to implement them by hand even less. Meanwhile, all the Smug Lisp Weenies™ are chuckling and mumbling something about "restarts".
What really kills me is that Go has a structured exception mechanism with panic/recover/defer, and it's actually better than try/catch/finally at that. It just doesn't seem to have caught on as the standard exception handling idiom.
> It just doesn't seem to have caught on as the standard exception handling idiom.
Not only that, but there is heaps of code which is correct in the presence of errors (after all, Go developers tend to obsess a lot about them), but leaks resources in the presence of panics. Those just fly under the radar of most Go programmers.
Good thing Go introduced defer. Because the panic-safe era (similar to the exception-safe era of C++) seems to be an era yet to come for the Go ecosystem.
And yet, this is exactly how you learn to design dependable systems. You try to stay in fail early territory as long as possible (bubbling up errors, failing every system level on the way), until you reach a layer where fail stop is not acceptable (or log and throw is adviseable since you reached a top level system layer).
It's at this point where you introduce a resilience layer, sometimes using a library like failsafe.
The most horrible designs and buggy systems I've seen in my career where the result of trying to mask errors in random places, making it impossible to reason about any of the code, and leaking resources left and right. The most stable systems I've seen started from a place where _everything_ that went off script resulted in the most horrible stacktraces, but taking proper care about cleaning up resources, and improving from there. Often, the top level resilience layer would only be configured for retries or failovers in (pre-) production systems, so developers and testing would keep getting battered with stacktraces, providing a chance to fix stuff.
Have you had practical success with this? It seems every discussion about Go error handling someone posts this but in all our projects it's riddled with false positives. I'm not convinced it's practically useful, though it is academically interesting.
As someone quite new to Go, who has found a lot to like, I find the lack of a built in way to model optionality (besides pointers, which rather muddy the intent) absolutely baffling.
I want to use values over pointers for as much as I possibly can; the sort of stuff I work on is not so performance sensitive that it would be a problem. But zero-values are totally insufficient for modelling optional values, especially for numeric data.
Now it has generics I could implement my own Optional[T] type, but backup from the compiler forcing me to check a value exists before doing anything with it is what really makes optional types shine in languages that have it.
It's always bizarre when someone on a discussion forum defers to the writings of someone else. If that someone else wanted to have a discussion, they could do so themselves. But since I'm in a silly mood, I'll play along with your silliness:
From the GNU C manual: "An enumeration type represents a limited set of integer values, each with a name."
Which is exactly what Go provides. Which isn't surprising as it is identical to C in that regard.
Enums suck, period. That's why many modern languages don't offer enums at all, providing sum types instead, which more accurately model what most people are trying to use enumerations for. It is an antiquated pattern that may have served a useful purpose in the early days of computing, but we've learned a thing or two about programming since then.
Well, at least you've come around to recognizing that Go does have enums, even if you needed someone else's words to say it. After all, Go enums can't suck if it doesn't have them. But, of course, Go purposefully tries to be antiquated. That's kind of its whole deal. Naturally it is going to have enums to really drive home the antiquatedness.
If Go had added generics from the start, it's very unlikely that the implementation would be as solid as the current one. I think their conservative approach paid off in this case.
I think you're making my case for me here :) Yes, Go reached maturity faster than Rust (despite its first release occurring later – one of the many benefits of simplicity), and the lack of generics doesn't seem to have hurt its adoption. Another victory for Worse is Better.
> Go reached maturity faster than Rust (despite its first release occurring later – one of the many benefits of simplicity)
Rust started as a research language by a PL guy working on his free time, he wasn't even suspecting his work would end up becoming mainstream. Rust only became somewhat serious several years after Go was released and used in production at Google. If you count Graydon's first Rust publication, you may as well count all of plan9 as part of Go's history! (Or how about using Go's repository first commit[0] if we're being facetious)
Also, implementing proper generics should have been straightforward from the start for Go, given that it was already mainstream in multiple programming languages back then (including the super mainstream Java and C#, with different approach). But Go was made by people who believe syntax highlighting is useless, of course they decided against generics…
It would have been straightforward to copy an existing approach to generics (though I suspect that this is a usage of the word ‘straightforward’ that has currency only among people who’ve never written a compiler), but I’m glad they waited and did something better that had a proper theoretical foundation. If they’d naively added generic types to Go from the beginning, you can bet there would have been all kinds of interesting unintended consequences, as there were in the case of Java.
"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.
If you take a step back here, the Go team acknowledged their biases and lack of in-depth knowledge in the domain, consulted with domain experts, and eventually settled on exactly the kind of generics system that you apparently approve of.
Could they have done this earlier, in principle? Sure. But at what cost? What other balls would have been dropped? I think they were absolutely right to focus on aspects of the language design and tooling that they were confident about. Go is all the better for it. It's the polar opposite of a language designed to appeal to programming language enthusiasts; and well, that shows in the reactions.
By all means use Rust. I use Rust sometimes too, and it has its advantages over Go. Personally, I find generics in Go an occasional convenience and missed them only a little before 1.18.
Then you don't have to create a new language. Golang was explicitly designed for not very experienced people to learn it and become productive quickly, even in big groups/teams with high attrition; that is: Google. And I think they reached their goal.
If Golang starts to add more and more features, it will lose this advantage and become just "yet another language". But worse: because of how was Golang designed, it will then end up worse than, say, Java. In other words, it will lose its advantages without really gaining much.
The problem is that this is probably inevitable, because the people that start as inexperienced developers with Golang will grow and become unsatisfied with the language features. Some more, some less, but that will be the general trend. And I think they are already vocal enough to push the things you listed.
Instead, I think it would be better for everyone if those people would instead switch programming languages and leave Go as is. Otherwise the cycle will repeat with the next Golang...
Think that is a misconception, that Go was just designed for inexperienced people. Rather it was designed to be easier to use than C++, from which the motivation to have something different came.
That a language is easier to use, doesn't mean it can't add features. The features are relative to how it helps the language be more useful and solve issues that users are having, so keeps it easier to use. Languages are always going to have pressure to adapt.
I didn't say that it was designed only for inexperienced people. But it was the or at least one of the major goals of the language creators that inexperienced developers can become productive quickly.
And the more features a language has and that a codebase uses, the longer it takes for someone to learn it and be productive (unless they can transfer knowledge from similar concepts of other languages of course).
But it was the or at least one of the major goals of the language creators that inexperienced developers can become productive quickly.
Do you have a citation for that? As far as I remember that’s as most a second order effect -- they wanted a language that felt as clean and straightforward as C, to benefit everybody including experienced developers, not primarily inexperienced ones.
I do indeed have a citation for that by Rob Pike - and no matter how downvoted I am, it stays a fact:
> The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
> Programmers working at Google are early in their careers and are most familiar with procedural languages, particularly from the C family. The need to get programmers productive quickly in a new language means that the language cannot be too radical.
> As far as I remember that’s as most a second order effect -- they wanted a language that felt as clean and straightforward as C, to benefit everybody including experienced developers, not primarily inexperienced ones.
No, it's not a second order effect. Or rather: both what you say (a language "as clean and straightforward as C") as well as a language for devs early in their career (i.e. inexperienced) are both second order effects from the underlying reason that the language is targeted at developers at google.
I would say that Rust does it pretty well with Option<>. You can define the logic when the Null should or can happen and you can avoid writing a lot of Error matching boilerplate, while still have a condition when something fails.
I might be slightly biased, because, not knowing about this project, I implemented half of the functionality in my own simpler module: https://git.sr.ht/~mariusor/ssm