Lies we tell ourselves to keep using Golang
fasterthanli.me
fasterthanli.me
But I don't find myself agreeing with his position, which is "you shouldn't use Go for production services" (he explicitly says this in one of his Go posts, I forget which one and don't have time to look right now).
The better alternative to Go is Rust. Okay, sure, I'm willing to admit that in the examples presented, Rust handles things better. But Rust isn't a panacea. Rust is complicated and hard, and often it's not worth taking on that burden just to theoretically handle cases that rarely occur and even more rarely cause any problem. Programming is a means to an end, and the cost of using Rust (hiring, increased development time) is often not worth it.
The reason Go is successful is because it's easy for companies to use to solve production problems with teams of varying expertise. Its stdlib is well-featured, its ecosystem is good. There's generally one correct way of doing things. The same doesn't appear true in Rust. For example, an _incredibly_ common thing to do in production code is to make a web request. In Go, there is no need for debate, you use the stdlib. In Rust, you have to use a crate, which requires a decision to be made. Even worse is the async story, in Rust you have to decide whether to use Tokio or whatever. That burden is just not present with Go.
Btw this article should not be flagged and it's pathetic that people have flagged it.
Go and Rust both have strengths and weaknesses.
And I think they both have a love/hate dynamic because they make pretty strong choices in their own ways, and so people react strongly in response. Personally I'm a fan of both.
I agree with this. I learnt Rust before Go, and using Go makes me feel like The Oatmeal piracy guy[1]:
"I'm not sure if I should use Go to write this HTTP service. I'd lose immutability tracking, I'd lose compiler-enforced thread safety, I'd lose the powerful type system, I'd lose the comprehensive error handling, I'd suffer from a million little papercuts, I'd have to use the weird date formatting system, I'd have to check nil pointers, I'd...
...oh, it's seven days later and I've already accomplished more writing networking servers and clients in Go than I ever have in years with Rust."
This isn't to say the points raised about Go aren't true. They are true, and if a better language were available, I wouldn't stand my ground and argue their benefits, I'd switch to it. The last comment I happened to post on this website is about how Go is insufficient without its army of linting tools [2]! Yes, I'm incredibly happy to have learnt both Go and Rust as their combination has expanded my skillset and the range of programs I'm willing to write tremendously. But if someone said to me "you should just use Rust instead of Go for your production services", I'd think the "just" was doing some incredibly heavy lifting.
An article that I'd like to see is one comparing the two languages for this niche (networking servers and clients), contrasting not just the language pitfalls but the third-party libraries necessary, the iteration speed, and the choices you'll have to make up-front. My guess is that the languages would be judged more closely together.
[1]: https://theoatmeal.com/comics/game_of_thrones [2]: https://news.ycombinator.com/item?id=30749921
"I'm not sure if I should use Go to write this HTTP service. I'd lose immutability tracking, I'd lose compiler-enforced thread safety, I'd lose the powerful type system, I'd lose the comprehensive error handling, I'd suffer from a million little papercuts, I'd have to use the weird date formatting system, I'd have to check nil pointers, I'd...
None of the modern language do it beside Rust so every language are then bad?
Java / C# / Python / Ruby so they fall in the same bucket as Go I assume?
For starters "without null" is a myth. They both have null. They have to; there is no other practical option. The JDK uses null all over the place, so you need to have null in order to talk to the JDK.
Now, they do still have mechanism to make null easier to handle. And they're both pretty impressive designs. (Especially Kotlin's, though I haven't tried Scala 3 yet so maybe I'm missing something wonderful there. Aesthetically, I just prefer "we're going to openly acknowledge it and tame it as much as we can" over "we're going to try to sweep it under the carpet.") But there's a sort of Amdahl's Law analogue hiding in that situation: the upper bound on how much practical null safety you can achieve is constrained by how much you can avoid relying on modules that were written in Java. And I know that's basically true of languages like Haskell, too, because you often have to rely on at least a little bit of code that was written in languages like C. But it doesn't preoccupy me the same way it does in Scala or Kotlin, where interacting directly with Java code is a much more everyday kind of affair.
But, at the other end of things, teams that were already using Java and want to start incorporating Scala don't have that option. And 10 year old Scala projects didn't originally have that option, and doing something about it now may be a lift on the scale of a complete rewrite.
Not just because of the null problem. It's also that option types push you toward a "conditional logic everywhere" way of doing things, because that's how you handle the options. That's all well and good and holy in a functional language, and perhaps even a procedural one. But it's the opposite of good object-oriented design.
To quote Dr. Mark Crislip, when you serve cow pie with apple pie, it does not make the cow pie better. It just makes the apple pie worse.
BurgerWithFriesMeal subclasses BurgerMeal?
"Object oriented design", in the religious sense, is an obsolete 1980s fad that took a good idea (encapsulation of mutable state) to comical extremes.
The original idea of OO design was to strive to eliminate stateful idioms. Which is a slightly different idea than what we're used to. The state existed, but the point was that you were supposed to design your system so that objects didn't need to know - or even attempt to infer - information about other objects' state.
The whole intellectual lineage that includes pervasive use of explicit state querying and manipulation methods such as getters and setters could be characterized as a whole lot of procedural programmers collectively missing the point. It's right up there with when people over-use the State monad in Haskell, effectively doing their darnedest to Greenspun imperaive programming on top of a lazy functional language because they haven't quite internalized this new paradigm yet.
So what is good OO design? NPEs? Nil checks?
- nil
- None
- Some(nil)
- Some(non-nil)
As far as how to do good OO design, ideally you try to avoid explicit branching whenever possible, and instead use dynamic dispatch to decide what to do. In principle, if you've architected things well, you should generally be able to avoid conditional branching.An ironically useful example of how this works is Smalltalk's implementation of Booleans and conditionals. Smalltalk doesn't actually have an if statement. Instead, it has a Boolean class that defines three methods: ifTrue:, ifFalse:, and ifTrue:ifFalse:. Each takes one or two blocks, which are effectively anonymous functions.
And then the implementation is that the True subclass as an ifTrue: that executes the block, an ifFalse that doesn't, and an ifTrue:ifFalse: that executes its first argument. And the False implementation does the opposite.
This isn't meant to be an example of "look, OOP doesn't need conditional branching, just use {library implementation of conditional branching}," so much as a small, self-contained example of the kinds of ways that you can achieve conditional-style logic without explicit branch statements. A more real-world example might be something like having separate NotLoggedInUser and LoggedInUser classes that behave differently in relevant situations, rather than having an isLoggedIn field to have to keep checking at every use site.
The big thing working against us on this is that most the popular OO languages - C++, Java, Python, C#, etc - come from the same approach of trying to layer object-oriented features on top of a procedural core. In my not-so-humble opinion, this has been about as successful as more recent efforts to support functional programming by pulling a mess of functional features into existing OO languages. Technically it gets you somewhere, but the resulting language is not an ergonomic pit of success.
Kotlin code requires you to use Type? if the value can ever be null.
Java code that is annotated by @Nullable/@NonNull will automatically map to Type?/Type (and it is up of course to the developer to not fuck up their nullability promises.)
Java code that is not annotated is a Type!, and encourages you to be wary about what could happen.
The nullability story is miles better than Java.
Additionally, all Java code (including the one you wrote) is only optionally null checked. All Kotlin code _is_ null checked, no matter what.
- Know about it (first, big problem for many java shops)
- Integrate it with Gradle/Maven/Ant/yourbuildtool
- Enable the null checks because they are not enabled by default.
Compare this with "it's already in the compiler"
A long time ago, in a Haskell community chat, I saw someone dismiss Go with a pithy comment along the lines of, "Go isn't a programming language, it's a DSL for writing network services." I think I may need to re-assess that comment as actually being a really compelling elevator pitch for the language.
Armed with that perspective, I'm seeing why I wasn't terribly convinced by the article's specific complaints about Go. "Traditional IPC is a PITA and forces you toward talking over a socket? Well, yes, exactly. That's kind of the whole point."
Sometimes I wonder if we are all suffering unnecessarily because of our incessant demanding that all languages try to be all things to all people.
Programmers aren't. Programmers are building stuff and talking about that. Opinion bloggers are suffering for clicks.
Personally I lean towards strict compilers (I suppose years of JavaScript has traumatized me), but 5x dev time is a big tradeoff! Of course this is just one data point, but it did seem worth mentioning.
Golang gets out of the way in me doing what I want.
Rust actively resists me doing things I will later regret.
Also, unexpectedly, I have gotten some positive comments on my C coding style after I coded some Rust.
All of this is completely anecdotal and personal experience, of course.
Trillion dollar companies run on "incorrect" software.
It is impossible to run "correct" software, but we can do better, maybe?
> often it's not worth taking on that burden just to theoretically handle cases that rarely occur and even more rarely cause any problem. Programming is a means to an end, and the cost of [Edit] adding if err != nil to every line of code [/Edit] (increased development time) is often not worth it.
I basically like Go, I really want to like Go, but as a Python person who has used exceptions with good success for 13 years in HashBackup, I can't see myself adding an if test to practically every line of Python code, just to catch an exception that might occur once in a blue moon.
A lot of software is not so mission critical that lives depend on it. If that is the case, sure - add error checking to every line of code. But for a lot of software, IMO, it's too much work for too little reward. If something blows up in production in an unexpected way, sometimes that's okay. You log the problem, fix it, and it's fine.
I re-read the Go error handling page before posting, because maybe it's not as bad as I had initially thought. Nope, still looks pretty cryptic to me:
The problem is if it doesn’t quickly blow up, but instead silently corrupts data or causes other problems down the line that are hard to backtrack to that specific missing check.
try:
do something
except Exception, err:
pass
If a piece of code is has a try/except, then it's because there is the expectation that it might fail and certain kinds of failures can be handled. If a failure is not one of the expected failures, then do a raise and let the exception go higher up. It's either going to hit a handler in the main program or will exit to command level. In either case there's a stack trace to see where the problem occurred. Exceptions tell you exactly where things blew up, whereas the "if err return" paradigm only does that if the test at each level adds identifying information.I've seen some Go stack traces. Talk about hard to backtrack: the ones I've seen go on and on. I understand it's because there are multple Goroutines running and each one has a stack trace, but they look pretty daunting to me.
as the person on call for such events, it's really not fine.
would you rather handle these errors upfront during development time or unexpectedly and uncontrollably, during runtime? having been on-call in one way or another for ~10 years, i know which i'd prefer.
after having used golang in production, i will never go back to using a dynamically typed language if i can avoid it. almost 0 thought or effort is required to handle errors correctly in go, and it's still possible to ignore them (just use `_`). not that i'd recommend ever doing that.
Exceptions are in my honest opinion better on every single front. Checked exceptions would be the panacea but Java’s version is not the best due to subclassing. But overall bubbling up exceptions is the correct thing to do — in most cases a problem can’t be meaningfully handled at the call site. Let’s say you want to read from a file during a HTTP request but the read fails n methods deep. There can be any number of other error occurrences and the only sane choice here would be to return some error code to the web client requesting the site. Why do you want to handle it n times instead of only at the goal?
(Ab)using Result types without good syntax for dealing with the very common case of “handle it higher up” (Rust’s ? macro is ok, not perfect but it is a much better compromise) is coming from a wrong angle imo.
ruby:
begin
res = Net::HTTP.post_form(APIURL, ...)
puts res.body
rescue e => e
# do error handling
end
go: response, err := http.PostForm(APIURL, url.Values{...})
if err != nil {
# handle PostForm error
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
# handle ReadAll error
}
fmt.Printf("%s\n", string(body))
Now, for which errors do you handle? In golang, you can see clearly where errors may occur. In Ruby, it's unclear whether HTTP.post_form is even capable of throwing errors, much less what they are. For the record, here are a few of the many types of errors that can be thrown by the ruby code. Here are a few: Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, ...
there's no way to tell _where_ the error will even come from - you might get a Net:: error, you might get a Timeout:: error, hell you could even get an Errno::! What most programmers wind up doing is handling no errors at all until they occur in runtime - or wrapping every single "suspicious" statement in a catch-all begin/rescue block, even when it's not needed.In golang, each function capable of returning an error simply returns it, and you must handle it at that point in time. It is very straightforward. In my experience, it results in much more reliable code. In Ruby, you have no idea what's coming. Do you see what I mean?
``` try { var response = http.postForm(APIURL,) } catch (Exception e) { # handle PostForm error }
try { var body = ioutil.ReadAll(response.Body) } catch(SomeSpecificExceptionType e) { # handle specific exception differently } catch(Exception e) { # run for every other kind of error }
If a given method is defined as `void postForm(…) throws IOException` you have to handle the exception at the call site, or mark the containing call itself with a similar throws clause.
I really don’t see anything not handled by these. Also, the defer part can be done by a try-with-resources construct that will clause the resource in every case, whether exited without exception or with one.
Been programming Ruby for 8 years and have never seen anyone do that, so no... If that block of code can really throw so many different types of exceptions (usually - it can't) you can always catch StandardError.
I think because it sets up the right psychological incentives. Because almost any option other than those two ends up looking like, "Hmm, I don't know how to handle this, but maybe someone else will, so let's bubble it up and hope they do," in practice. It's just the way we humans are.
And that sets up an intractable moral hazard situation. It creates this middle ground where two different owners of two different parts of the code can finger-point and blame and try to pass the buck. And, knowing that (though probably only unconsciously), they won't just become sloppier about handling errors. They will become sloppier about allowing errors to happen in the first place, and doing things that make it harder to handle errors. And they won't even know they're doing it.
That's what I think of when I read your hypothetical, "Let’s say you want to read from a file during a HTTP request but the read fails n methods deep." That sounds to me like code that's been structured in a way that, on this team I was alluding to above, probably wouldn't have happened in the first place and definitely wouldn't have made it past code review. The pushback would be something along the lines of, "Why on earth are you doing both file and network I/O within a single business operation but at two very different depths in the call stack? Did you even notice what you were doing here?"
Here's a counterexample from HashBackup (I'm the author):
A filesystem is being backed up. Some weird error occurs, let's say it tries to read an extended attribute and gets some goofy error that raises an exception and it's not in a try/except. That error bubbles up to the try/except for "save a file", prints the exception message, and continues by backing up the next file.
Is it more reliable to crash when reading an extended attribute, aborting the whole backup? Or is it better to just tell the user about the error, finish the backup, fail at the end of the backup with a non-zero exit code, and report it in an email?
At the time of failure, the only way I can see that the error could have been handled at the call site would be to return an empty extended attrbute. To me, that's more like the "silent but wrong" solution. And there are many of these situations for saving a single file: get the flags, read the extended attribute, read the permissions and times, figure out if it's sparse, etc. Instead of having error handling at each of these steps, they can have no error handling and just raise exceptions and the high-level "save a file" handler will do everything.
Of course, along the way there are some low-level try/except blocks around code that needs cleanups, like:
fd = None
try:
fd = os.open(...)
do some stuff
finally:
if fd != None:
os.close(fd)
But you have the same thing with defers in Go.The takeaway - if you gloss over the ranting and look for the good bits - seems to be that you should choose a language that handles a lot of the boilerplate for you (e.g., no verbose error handling) and has FFI.
It's not a bad argument, and it's making me think about how and when I use Go. For that, at least, it's worth reading.
I think this is going to change. I couldn't find the relevant blog post now, but I believe to remember there was an initiative to provide a standard async runtime.
What is planned is to standardize the API: that is, provide common traits for IO, tasks, etc. so libraries can be runtime-agnostic and you can change your runtime by only touching Cargo.toml (hopefully).
It seems that C is a language/compiler/toolchain combo that lets you shoot yourself in the foot, but gives you enough access to stuff to be maximially performant in a random (i.e. not specialized) use case. (C++ is in this camp too).
Rust is a language/compiler/toolchain combo that tries its best to not let you shoot yourself in the foot by presenting those footguns for you to fix up front, without giving up much of the access to stuff (C++ tried some things in this camp like destructors and smart pointers. GC is also something that's in this camp)
Golang is a language/compiler/toolchain combo that lets you shoot yourself in the foot in some circumstances, and does not give you enough access to be maximally performant in every random case, but rather it seems to posit to give you opinions that will get you really, good performance in most random cases (if you need to tune the GC, for instance, you're probably worse off, like here: https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i...). Meaning it's making the tradeoffs of upfront complexity vs runtime issues, and Tim Toady vs The One True Way for you, because in the general case those are the most optimized tradeoffs. I don't disagree, especially when the closed-world ecosystem is good enough to not need to reach out to external tools whether those are via cgo or in the build toolchain.
In general, I think that if you reach the point where Golangs admittedly major warts are hindering your progress you're doing better than most companies, so its succeeded in not getting in the way of progress at least until that point.
In particular since the article keeps comparing Golang to Rust, for personal projects I do like the correctness of Rust, but some things are frustrating, like async being basically unusable without Async Drop, an uncertain future for completion apis, and apis that really want HKT but can't use them (yet?). I having to make up a lot of opinions that Go makes for you adds risk and uncertainty to a project where the most likely failure modes are not, in fact, those opinions.
I'm excited for the future of Java, with Loom and Valhalla coming to address as what I see as two core problems of Java in particular and other languages in general. I'm also excited for the present of js/node.js/maybe Deno. typescript has become good enough that I also think it's a really good competitor to Golang in the "just write code" arena, especially since there's significant synergy between the front end and the back end to help with build tooling, validation logic, and programmer expertise.
* First you construct a request, and check the error on that
* Then you "Do" the request and check the error on that
* Then you check the status code (you did check it, right?)
* Then you deserialize the response and check the error on that
* Then you close the response body (you did remember to close it, right?) and check the error on that
Actually making an HTTP request with Go stdlib is a many-stage process and it is very easy to screw up or forget one of the steps. I see it all the time. Actually I have seen outages related to people being too zealous and reporting errors related to closing the response body on otherwise successful requests.
I kinda get it though, because the compiler or linters will show that as a warning or error going "bruh you forgot to handle this error", and as a perfectionist one wants to get rid of all of those.
I mean IMO those should be logged anyway, if only so you get some data on how often it happens, if there's anything that can be done about it, and if it's harmless, to ignore the error - but make it explicit.
Nope, a better alternative is Java/C#/Kotlin/etc. Those offer sum types, async programming, while being more productive than golang, and provide superior tooling and a superior developer experience.
And yet, Go's HTTP client doesn't have a good default [1] because of zero values, which are argued harmful in the article.
[1]: https://medium.com/@nate510/don-t-use-go-s-default-http-clie...
Now suddenly we go from a poor type system (Java) to a decent one in Go (this is my personal opinion). Or from no type system to SOME type system. On top of this, we are getting all the benefits of a compiled language.
Essentially this is looking Go as a system language, while I think of it as an application language.
Aside from that, the author is right in some ways.
No, the better alternative is thousands of different languages.
HN is just hype-driven as usual and can see only Go and Rust.
It would've never got to where it's at if it wasn't for Google, that's the most important reason why its successful.
In my experience it is only increased initial development time.
I find that initial development time is frequently conflated with development time overall.
So obviously worse is better languages will be preferred.
You may say it's the snark/anger/frustration that got flagged, but I suspect it would not have been flagged if the topic were different.
Presuming the author is making bad-faith arguments for internet blog points goes against the spirit of HN. I prefer to draw my own conclusions, thank you. I normally expect HN to take the higher ground with calm and reasoned counter arguments of the content but not today I guess.
Any criticism of sacred icon is taken like an aggression to the reader's sense of self.
HN has had plenty of threads about Go over the years. What's different here is that there was just a big Go flamewar yesterday (https://news.ycombinator.com/item?id=31191700), from the same site. Having another big Go flamewar the next day is a really bad idea, because then on top of shallow ragey flameposts (boo) the thread will fill up with fluffy ragey metaposts (double boo). More explanation here: https://news.ycombinator.com/item?id=31207126
"Shameful censorship" is one way to put it, but front page space is the scarcest resource that HN has (https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...) and the amalgam of upvotes, flags, software weightings, and mod actions is what determines which stories end up / stay there vs. which stories fall off. This is the cycle of life on HN—it's always been that way and always will. One psychological effect is that everybody feels like their favorite topic is under-represented (or shamefully censored!) and in a way everybody is right.
I've often joked by adding "even Rust hackers feel that way" but maybe that's not the best line to use in this case :)
p.s. I feel like it's been a long time since HN had a passionate explosion about flagging and censorship (side note: ctrl+f misinformation...yup, that too) around a programming language. In a way it feels like a healthy sign. Like a family fight at Thanksgiving - at least it's about, say, a board game or something.
They don't have to be correct, they just need to provoke meaningful discourse, which for me means crowdsourcing the insights of hundreds of fellow devs.
The system has checks and balances to prevent such things from dominating discussion here. (Flags are part of that system; so are software bells and whistles; so are moderators). If we didn't have those, the front page would consist of nothing but sensational flamewars and endless towers of meta!
Given the options I know of today for authors, if they’re still reading comments here, I’d recommend they replace the 2020 post with a redirect to the 2022 post, and include the 2020 post at the end of it fully intact. At least that would ensure the flamewars cycle on the complete conversation and not just half of it.
Sending it to page 2 when it's only 3 hours old and has more upvotes than almost everything on page 1 /and/ removing the [flagged] marker feels weirder to me than not doing anything, but eh.
I can appreciate your point about having limited resources to moderate posts that have a track record of generating big flamewars. But I didn't get the impression from the comments (many being ad-hominem) that the post was flagged for that reason and my use of the word "shameful" was directed at those comments.
I haven't read all the comments on this one yet - there are quite a lot of them already - but reading this makes me think that perhaps we don't see the same comment section. (Perhaps because I'm not tasked with moderation, so I don't have much reason to go look at the 2nd page.) They've mostly been very thoughtful and balanced. It strikes me as some of the highest quality discussion on the subject of Go that I've seen on this site in years. It's a shame it was flagged and then buried on the 2nd page. A pinned comment reminding us not to get our hackles up in response to the author's abrasive style seems like it would have been plenty sufficient. I fear that, in your haste to head off an anticipated flamewar, what you've actually done is suppressed a potentially healing example to the community that it really is possible to have a mature conversation about Go.
The and in the previous paragraph is important, though, because a core moderation principle is to de-emphasize generic discussion (and follow-up threads, and repetition generally) in favor of significant new information. Past explanations on all that, if anyone cares:
https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...
https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...
https://hn.algolia.com/?dateRange=all&page=0&prefix=false&so...
https://hn.algolia.com/?dateRange=all&page=0&prefix=false&so...
If a flamewar is a problem, then that's what moderation is for, including potentially locking. I know you've posted comments on divisive articles before cautioning everyone about not wanting the comments to devolve into a flamewar, that would have been a great first step here.
But instead of doing that, you're saying that because of the old article popping up again yesterday and rehashing the same old flamewar, you've chosen to suppress the author's own response. I would think that the old article popping up yesterday makes this new post especially timely and even more important. It's not a rehash of the old flamewar, it's the author's own words with a well-written and fairly comprehensive response to the common criticisms, and it's highly relevant to the HN audience. And especially in the context of the discussion yesterday it seems a good idea to ensure visibility of the author's own response so anyone who saw yesterday's flamewar can see this. It took 2 years from the old post for the author to write this response, I'm pretty sure you don't have to worry about having a third post tomorrow.
The sheer number of upvotes on this article, despite the attempt at suppressing it, is a clear indication that, regarldess of what dang thinks, a lot of people in the HN community think that something that is most definitely not boring and worth reading has been said.
I'm inclined to agree? I personally do tend to agree that golang in and of itself is kind of a boring horse that's been beaten to death at this point. But this article was interesting, anyway, because it had an interesting and thoughtful perspective on what kinds of things matter when choosing a language ecosystem more generally. One that I, as a hobby language designer, was glad I read. Even the bits I don't think I agree with. Even some of the bits that seemed unnecessarily invective.
That's fundamentally not how HN works, though. It's neither just crowdsourced nor just editorially curated. This thing ends up being a dupe fairly straightforwardly, it's not some weird edge case. Any big front page discussion naturally has a lot of people interested in keeping the discussion going.
I admittedly haven't read "Mr Golang's Wild Ride" since it first hit HN a couple years ago (and I don't really intend to, I have my doubts that it would be an edifying use of my time) but, based on what I remember of it, this new article is a much more nuanced and thoughtful position on the subject that definitely adds a different perspective to the conversation. It focuses on higher level questions about how one designs (or chooses) a software platform and ecosystem, where the original one was a fairly shallow fisk of language design issues. Wasn't it mostly drumming on syntax?
I'm not sure what "weirdly hung up on the content of the piece" is supposed to mean. Isn't the whole point of Hacker News to share and discuss interesting content? If it's not about digging into full, long-form content, then we might as well take this to Twitter.
I'm also not sure what you mean by "this one had significant discussion yesterday, last year, and also the year it was published." The article was posted on Friday, April 29, 2022 - today, to be precise.
A really simple but slightly different way to think of it is, if you write a piece and it gets good traction on HN (where you've also participated in the discussion, in this specific case), you don't usually get to put a megacomment reply on the front page the next day as well. There are exceptions, of course but 'things someone likes and dislikes about a popular programming language' are generally not the exceptional type of thing.
Perhaps you didn't personally get much out of the article. I did. I didn't love the invective aspects of it very much, either, but there were still some interesting take-aways and new things to say. Apparently enough so that 593 people so far thought it worthwhile to click the "upvote" button, which is really quite a staggering number when you consider that it had been banished from the front page. When you've got that many people seeing value in it - that's really quite a lot more than most of the rest of what's been on HN today - even if you don't personally see why, maybe it's enough for you to simply not see why, and leave it at that.
And that is what I am getting at. I can see the logic of the moderation just fine, but I think that the logic in question is wrong and perhaps even edging toward paternalistic or mean-spirited. And my interactions with dang on it elsewhere in the thread strike me as being mostly just defensive. Which doesn't really jive all that well with the stated purpose of elevating the quality of conversation on HN. I'm more impressed with what the broader community has done: largely looking past the flamebait, eschewing "shallow dismissals", and instead choosing to "respond to the strongest plausible interpretation of what someone says."
I don't follow. You weren't familiar with the rules of HN dupery and I've tried to explain them. That's not moving the goalposts. Followups are almost always dupes. Metaish follow-ups the very next day dupely so.
With all due credit to rmasters, "I suspect it would not have been flagged if the topic were different."
Please don't strawman me like that. If you're going to make "the rules of HN dupery" the place where you want to make your stand, then this seems like a peculiar response. These rules for HN dupery are not actually published anywhere outside of HN mod team comments when debates like this come up. By contrast, though, the published commenting guidelines clearly state "Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith."
Which, I realize that this whole tangent is then in contrast with the very next guideline in the list, which exhorts us to "Avoid unrelated controversies and generic tangents." But, since HN lacks the meta-discussion channels that other comparable community sites have, these sorts of frustrations can't really do aything but boil over into public every so often, so I'm self-consciously allowing myself the conceit. In my defense, I've also been following and engaging in what I thought was some of the more interesting HN discussion I've seen in a minute. Including having my opinion on a programming language and its community go through some positive changes. So I do somewhat feel, then, that HN moderation deciding to flag the article and then bury it on the 2nd page because they think it's just a flamewar is tacitly an unfair accusation against which I must defend myself. And, frankly, the alternative defense that it's because it isn't interesting comes across as dismissive and condescending.
From the way you've written on this thread, it seems pretty clear that you're not the kind of message board nerd that makes 'dang's comments a daily read. That's fine! You have to be a pretty extreme nerd to do that, and there other, probably better things to spend those nerd points on. But you should take 'pvg's word for it on this stuff. He's not messing with you: he's trying to explain to you how the site works.
I'm not sure who you're quoting here but it's not me.
US political and COVID articles
These also get moderated quite a bit but it's a little different with ongoing hot topics - many more submissions, angles, etc. One person's take on Go, however interesting, is not an ongoing hot topic. Plus it's had a pretty good life on the site!
Also not meant to diminish the blogger but if you check their posts, it almost seems they feed on Go flamewar.
I could read every one of the 443 comments and still come out with zero knowledge gained because that's the kind of comment these posts incentivize.
Startups were built with less man-hours than what was collectively wasted on this thread.
The way `nil` can seep into your codebase in Go and how bad the FFI is in Go will prevent me from ever choosing it for a project going forward. And I personally would not feel confident modifying Go code written by someone else, and would not feel confident accepting PRs into my Go codebase unless I understood every line inside and out.
Rust is a breath of fresh air in that regard.
All that said, I think the author here is idealistic in a certain sense. In the scale of "solving a problem by hand (or using Excel/a proprietary tool etc)" to "writing a computer program to automate it", both Go and Rust are very close to each other. And once someone learns Go and knows it well enough, the incremental cost of learning Rust or even thinking about the problems Rust solves for you becomes not worth it. I think that it's sad that this is the way it works, but that's just how it is. It's easy for settle for a local optima like Go when taking into consideration family, pets, salary, time, effort etc.
I do think on a long enough timescale, programmers will eventually move to a "better" language. But in my opinion, that time scale exceeds the lifespan of a human :)
These days I find myself leaning heavily on either Haskell or Elixir/Erlang's ecosystem (with a preference for the latter). Or JavaScript (there's just an order of magnitude more jobs)...
If you're an automation-oriented kind of person like it's my job to be, setting up repeatable toolchians based around the above is a total crock of shit.
Every non-Rustacean who has suddenly had Rust forced into their workflows asks for this to be solved, specifically, and the Rust community keeps digging its heels in.
Haskell has its warts but it's not 101-level shit like this.
If you want to isolate your build machines from the internet entirely, you're either going to use a hermetic build system and have some equivalent of vendoring your compiler, or manage your own internal package repository in which case you can just add your own Rust + Cargo build to it.
If you're paranoid about building the Rust compiler from source, you can even put in the effort into bootstrapping your own lineage. But given that you're ok relying on your package manager for Haskell, it seems like this isn't a huge problem for you.
Zig exists and is here today.
My whole career people have been telling me to stop using languages or tools I've been productive with. I've built successful companies where everything was written in python, the whole time people on HN telling me how I was using a lowly language because of things like whitespace significance or strings that were not unicode by default.
If you care about other stuff, fine. If you don't think go works for you, don't use go. Just stop telling people who are using go (and other productive languages) to stop using them because of obscure language design issues or obscure API choices that people rarely encounter day to day. That isn't what matters to most people, and it's actually bad advice.
The problem is that there are a ton of less experienced people reading stuff on HN (just as I once was) who will take this to heart but have nothing actionable to gain from it, except to think they're a bad developer using a bad language. They aren't, and they're not.
This isn't an argument against Go, moreso that your perspective is objectively elitist.
I don't think we should be so cynical as to just lie down and accept that some things are just too hard to get right
This is what Hacker News doesn't want to hear, and so needs to hear.
Like obsessing over the school supply list at the beginning of the year and getting everything perfect; it's not the whole of success, nor is it even really a huge part. But it can be fun.
In the end, even at mostly software companies, if you don't have a good sales model, a good sales team, a good marketing team, its likely you will out of business soon.
1. Get a sales team that could sell shit on a stick.
2. Get a programming team that can do a bit better than shit on a stick.
In reading this article, I'm finding myself agreeing with 100% of the author's criticisms. I've seen every specific problem mentioned be a thing that bogs companies I've worked for down and erode productivity. A lot of time the ops peoples' (aka: my) productivity specifically.
I haven't worked at a single company where teams of developers using Golang have been able to get the basic networking stuff right.
"Why are my TCP connections not closing? Why am I encountering port exhaustion?" Go's frustrating inability to inter-op matters quite a lot at the end of the day. GRPC and protobufs are f'in messy and full of footguns.
Thing is, I've been around the block. I know that these problems don't have to be problems. We have a tremendously bad tendency in this field to join cargo cults.
When looking for my next gig, if the company is big enough, I'm likely to start seeing Go as a negative, unless they've got that Tailscale kind of expertise.
This has nothing to do with Go. You're implying Go has a bug in its TCP implementation which I assume is false.
Networking works just fine in Go and it's actually easy to use.
Edit: I'm getting downvoted but please share with TCP issues in Go.
No, the parent is implying that Go's TCP implementation is easy to use incorrectly. Specifically in ways which cause the aforementioned issues.
It's not "TCP issues in Go" it's "Go leaves you to figure these things out for yourself and write bad code that doesn't work if you don't thoroughly understand its gotchas."
Also, if you've been following Go over the years, you'll know that there's basically _always_ open issues (https://github.com/golang/go/issues/39063) in net & net/http about how Timeouts get mishandled and connections end up not being closed.
Cloudflare even had to write a whole (now old and outdated as hell) article explaining how to "do it properly" https://blog.cloudflare.com/the-complete-guide-to-golang-net... so that you don't footgun yourself.
And the ticket you show does not show any issues on the Go side.
So… why?
Distributed data modeling is inherently messy and IME gRPC is one of the least messy, most scalable solutions to it. Or do you have a better alternative?
I used to be quite productive with PHP. That does not mean PHP is a good language.
> The problem is that there are a ton of less experienced people reading stuff on HN (just as I once was) who will take this to heart but have nothing actionable to gain from it, except to think they're a bad developer using a bad language. They aren't, and they're not.
You are replying to an article mentioning many of Go's problems, without rebuking any of the article's points. Perhaps these poor people are using a bad language? (Also perhaps some people reading this are "bad developers", whatever that means? Perhaps I am! What does that matter?)
Sometimes, a poor language is a good tool for specific task despite of being generally a poor language. Sometimes, knowing and liking a bad language makes you use it for tasks it's not suited for. Go is in the same category as JavaScript and PHP there, which have their valid uses, but also a lot of valid reasons to stay away from. You only learn how and when to choose them once you become experienced enough in several languages to notice the differences between them over long term usage and project maintenance.
I was looking into a new language to learn outside of JS and Python. I wanted to learn a hot language. I did some digging between Rust and Go and found out that Go is more suitable for web backends, CLI apps, etc. while Rust was more of a contender to C/C++, as it was primarily was made to be used as a memory-safe, correct, strict language to write low level software.
I don't generally involve myself in the latter so I chose Go. I definitely want to learn Rust someday but is it worth it if I don't get into that low level of things?
What are Go's and Rust's target applications according to readers here?
A big thing people learning Rust do my mistake is to try and use all of the low level features straight away. Rust has tools like Rc, RefCell, Arc, and RwLock that let you have a garbage collected language (well, reference counted) and not worry about any of the low level memory management details.
See things like https://ggez.rs/ for games and https://www.arewewebyet.org/ for web stuff.
Although honestly I think if you're looking for a "hot" backend web language I'd say Elixir is the more well designed one than Go.
I'll definitely learn Rust one day and make stuff with it when I have proper time and I'm not learning anything else.
Thanks for the insights!
Rust is a complex language and its APIs tend to reflect the underlying domain complexity nearly 1:1. It also does not have a GC. Rust interops with native code well. When you're writing code that needs to be very correct especially in complicated problem domains, Rust is a great language to reach for because it bubbles up that underlying complexity so well. If you really need the lack of GC pausing then Rust is also IMO much better than most other non-GC languages in common use (c.f. C, C++). If you need C interop, then Rust is great. If you're a fan of writing elegant abstractions, Rust is also a great tool as its macro processor and language constructs make it easy to write abstractions (compared to other imperative languages at least). But its use for other areas is, IMO, a bit fraught.
Go is a lot more opinionated as a language. Its stdlib hides the complexity for certain interfaces. It has a simple-to-use concurrency model that the whole language opts into. It compiles quickly. It produces a no-nonsense static binary and has a great cross-compilation story. Go code is repetitive but simple to read. Go's tooling is stellar and because of how easy it is to write third-party tooling, people often build useful tools for themselves. I find it really easy to hack on or debug other Go projects if needed. Overall, Go is opinionated, and if the applications you're writing and the style you're writing them in fall into Go's opinions then you'll love it. If you don't like Go's style, you'll hate it. Go is my go-to (pun not intended lol) language for hobby coding because when coding as a hobby I'm often working on a constrained problem and don't need Rust's complexity.
To offer a concrete example, the OP in his previous blog post wrote a diatribe about Go's filepath handling on Windows, but when I recently used Rust to write something that templated a few filenames, the whole thing took incredibly long. The complexities of Rust filepaths accurately reflect all the edge cases available for different platforms but was ridiculous overkill for my simple app that I was hacking on that was only ever going to run on a single Linux x64 platform.
IMO to just "get things done" use Go. If you find yourself fighting Go's idioms, then Go isn't for you. If you really enjoy writing elegant abstractions, Go isn't for you. If you need to go deep into an API that Go simplifies and will spend most of your logic doing just that, then Go isn't for you. If you can't tolerate the GC pauses then Go isn't for you. Otherwise just use Go to get stuff done.
Indeed, there's no straight-forward answer to the question "Should I learn Rust as a web backend developer?". It depends on all of the above things you mentioned here.
Go: Trades off correctness with opinions and abstractions. Has a GC. Has a simple, dare I say old or not-modern, type system. Easy to pick up. Hard to have full control over the program.
Rust: Reflects and embraces the actual system with its complexity. Correctness is first and foremost as it deals with all the edge cases. Not easy to pick up. But worth it for critical projects.
In a realisation, why do people even compare these two? :)
That's the puzzling thing. While the author doesn't explicitly call out Rust as the better alternative, if you look at his Twitter, it's fairly obvious his community is thinking of Rust. But I don't understand why. They're different languages with different strengths and weaknesses. If I'm writing a net service I'd rather write it in Go. If I'm writing something in OpenGL, I'd rather write it in Rust. There's no need to insult one another.
If your definition of "productivity" is the time spent in programming, I think you and the author are not in disagreement. The point that the author makes is that using a language with sophisticated type systems prevents some categories of bugs to happen in the first place. That means less time spent in debugging and fixing the code.
I do think he's misunderstanding that the design intent is "networked C" and that Go + Protobuf is much better than he thinks. A lot of his complaints with the language and runtime boil down to:
- It's got a primitive type system (yep, nobody disagrees)
- It leaves a lot of problems to you (yep, just like C)
- you can't integrate it into existing binaries (yep, distributed systems connected by RPC)
He comes to terms with the last one and admits it, but it is not only the thing Go is good at but it was the design intent. If you start from "we want a language and runtime for writing distributed systems that would otherwise be written in C or Java" then Go is pretty good!
I'm a happy Go, Rust, C, and C++ person. They're each for different things. That Rust worked out (is working out?) despite being an incredibly powerful and complex language (compared to C) is awesome. In the mid-2000s, it's not clear that you'd bet on a language like Rust. It is clear you could do better than Java or C++ for distributed systems programming, and Go hit that pretty reasonably (I do wish the language had a better type system, and I'm glad to see Ian finally land generics, but I'd absolutely reach for Rust when writing code in a small group that can handle the "language complexity" in exchange for the benefits).
Sure, Java has some warts but it is a very easy and small language, the reflection-based frameworks are not at all mandatory, Java can be quite expressive without those as well and it is extremely performant. Once value types get included it will be really hard to get ahead of Java.
Let's start with bytecode. It "requires" (except for AOT work) a warm-up / JIT phase that tends towards slower start times. This isn't hard and fast, but in practice it's true. (And when you search for "jvm startup", you end up at lots of pages about how to tune things to try to make this better). For some people, just as bad or even worse (remember, distributed system) is the impact that JIT-compilation and warm-up has on tail latencies. The static nature of most compiled languages mean this isn't a thing you worry about. You've got enough tail latency problems in your life, you don't need "oh hey, I just saw this loop for the Nth time, I took 10ms to hot compile it!".
That leads into the runtime and some of the language choices around it. Java GCs are generally fighting a harder problem than Go's GC, and with a very different tuning point. Java making everything an object and support classloading and so on, has real knock-on effects for the amount of work required to get high-quality, low-pause GC to work. This is much better today than it was in 2008 when the Go team started, but it's still the case that Go has an out-of-the-box better GC experience. Yes there exist special, tuned GCs (e.g., Azul's C4, Shenandoah) but Java GCs have to really be super clever compared to the fairly simple strategies that work in Go; I applaud the ingenuity in Java GC work, but if what you care about is "reasonably good at collecting memory, never pauses for long", the Go language decisions made the GC problem easier.
I think it's fair to ask "Shouldn't Google have just improved Java?", but I think you miss out on lots of the concurrency ergonomics. Again, this has to be compared to a decade ago. Especially post 2010 (Oracle acquisition) it was super unclear that you'd want to attempt to "bet on Java".
To be clear, I do think Go is at the wrong point on the language/type-system sophistication curve for my tastes. That said, I find myself shaking my head at most Java code, while looking at the Go code and thinking "sigh, lots of repetition. I hope one day that gets better, but at least it's obvious".
Java is more intimidating to get in to. It starts with "Oh dear, which JDK do I use?", proceeds to, "Oh dear, now I have to pick a build tool and every single one has a near-vertical learning curve by modern standards." Then you're on to, "I want to do X. Why are there 28 competing libraries for X, and why are the most popular ones invariably the most hated?" And so on.
There's a strong case to be made that procedural is a better paradigm than object-oriented for a lot of the niche that Go targets.
You mentioned concurrency ergonomics, but I just wanted to emphasize that I doubt that Java's concurrency story can ever be a match for Go's. There's too much legacy baggage that can never be unloaded.
Project Loom is already targeted for preview for JDK19. Not only does it match golang's concurrency story, it is superior to it.
But my point around ergonomics still stands. There will still always be all the legacy stuff, and there's not necessarily any way around having to understand how that works, too, because there's generally going to be some pre-existing library or legacy code or whatever that forces you to.
> and there's not necessarily any way around having to understand how that works
Same with golang, you have to understand how things work. The entire narrative that golang is "simple" is just not true. The moment things start to go wrong, you're going to have to understand how things are working behind the scenes.
I don’t really agree about the 28 competing library thing though. First of all, Java has a really good standard library. Sure, it shows its age at some places but it’s not like you have to grab a new dependency for everything, I pretty much only grab deps for very specialized tasks, and in case of Java I now there will always be a match. The only difference in go here is that a bit more network-related code is in the standard library than in java, but that’s hardly that big of an advantage.
Regarding concurrency: the most unique feature of go is unarguably go routines, but the language has an otherwise quite dumb concurrency model hindering its usefulness. Sure, locking should not be the primary mechanism here, but it is painfully bad with defer being function scoped, not block scoped — while java has synchronized blocks since forever. And with the already mentioned coming of Loom, the JVM platform will again rise to the top with regards to concurrency.
It's not hard to choose if you know Java well, but this sort of thing is a huge barrier to entry for someone who is just getting into it, or trying to decide what platform to use.
The community doesn't help here. If you go Googling to try to figure it out, you'll find a mess of conflicting advice being argued with varying levels of passion. By definition, a newcomer doesn't have enough background knowledge to make their own decision, and they aren't necessarily in a position to know whether or not they really need to wade through all that dreck before deciding.
re: frameworks, it's a similar story. Java has a pretty big standard library compared to a lot of smaller programming languages, but it's really not much different here from other platforms that punch in its same weight class, and I believe that includes golang. But the standard library is background noise; if Go's on the table then I've clearly got a job to do, and that job is probably something like writing Web services. And 28 is probably an undercount of the number of Java service and microservice frameworks out there. With a lot of vigorous discussion among proponents of each. It's got to be just stupidly intimidating to face down when you're first getting started. By contrast, I wouldn't be surprised if, by choosing Go, one could have the whole job done in less time than it would take to compile a list of all the information it seems like you have to read before you can even dip a toe into Java.
Disclaimer, I don't think this translates into "Go is good, Java is bad." Personally, I would absolutely pick Java if it were a project I were getting paid to do. For several reasons, but mostly because I already know Java quite well and I know it can get the job done. And I suspect that Java has more general-purpose value than Golang does. But I don't think "Is Java a good platform?" was really the question. It was, "Why might someone choose Go over Java?" and that's a very peculiar question; one that I think you can only answer in an interesting way if you put yourself in the shoes of someone for whom it's a real decision to make - most likely someone who isn't too familiar with either.
It doesn't matter at the end, when those GCs work. There's also ZGC by the way, which has been consistently improving release over release, with sub ms pauses in the latest release for TB sized heaps, something that golang's gc cannot match.
> I applaud the ingenuity in Java GC work, but if what you care about is "reasonably good at collecting memory, never pauses for long", the Go language decisions made the GC problem easier.
Now if you want to have high throughput and willing to sacrifice some latency, Java's GC selection allows you to do that. Where as in golang you're stuck with the single gc implementation and have to resort to finicky code changes.
This is true, but relatively easily solved by AOT compilation which has been tried 2 decades ago as well as now and many times in-between. With a closed-world assumption done by Graal it is not a hard thing to do.
Re: GC
What language decisions do you mean? Other than having value types and some form of pointers, I really don’t see that much of a win. Sure, in some basic networking case allocating on the stack manually almost everything may be possible, but my experience shows that general purpose programs simply live or die by heap allocations and you can’t generally avoid them. And here the complex, but state of the art GCs of Java demonstratedly win big times, and this is the reason why the performance advantages of Go are not so pronounced or even existing for more complex use-cases.
But I will give you that the Oracle acquisition is indeed a good point - though it fortunately turned out to be a good thing for the Java ecosystem in hindsight.
Do you mean complex reflection-heavy java code or even simple explicit one? Because while I agree the latter is not the most common one in some areas, I find it to be surprisingly readable and simple, while still being expressive enough when needed.
C and Java are far from the same. I'd argue that Java addresses or solves many of the criticisms he listed for golang (sum types, pattern matching, immutability with records, superior error handling with exceptions, nullability tracking with annotations, resource handling with try-with-resources, etc.).
I've been coding professionally since 1987, using everything from mainframe assembler, Fortran, C, VB, Java, C++, to most recently Go. IMHO, the language itself plays a smaller role in its usefulness than most think. As important is the tooling, stdlib, ecosystem, community and "StackOverflow"-ability.
Go has a few warts, like every language, but not that many, and they are more than made up for by how easy it is to assemble and run a project using it. It's that that makes it the most productive language I've used so far.
As far as I know, no other popular language allows you to read from multiple queues at the same time, which is an extremely useful pattern in concurrent programming. The only way to "select" from multiple sources in other languages is to use some kind of poll/select syscall on file descriptors - and even that is very limited, since kernel (at least Linux) does not allow userspace programs to create private file descriptors for intra-process communication - they must always be bound to some file/pipe/socket.
Off the top of my head, that's certainly possible in Rust, C++ and Java - probably many others I just don't know about.
That particular bit of concurrency goodness comes from the 70s, so you can be sure other languages picked it up.
But does that matter? In all my experience, I can't think of any instances where that wouldn't be a distinction without a difference. (Perhaps I'm missing something, though.)
Where languages differ is what they make easy to use, or first class.
As an example consider Go's autoformatter gofmt. It got released with the language, pushed and marketed as the way to format your code - first class. As a result, virtually all Go code is uniformly formatted. Contrast that with autoformatters in other languages pre-Go. They existed, but often multiple per language, each with hundreds of configuration options, all with minimal uptake in their ecosystems.
(If your point was about it being _built into the language_, then it definitely stands, see the other discussion in the parent comment thread)
I guess I just like it being built into the language. Plus no function coloring (everything is asynchronous, but appears synchronous) really makes it easy to build complex systems.
That's a trivial thing in javascript.
Agreed, and things happening "at the same time" are possible only with parallelism. Hence my question =)
But sure you could also add parallelism using a few built-in Node modules (there's cluster and worker threads, also don't know if atomics are already in Node, they are in V8),
It's not trivial anymore but also not that hard. A newcomer could figure it out in ~30 mins from the docs.
During my PhD we had to crunch some numbers and my group usually makes use of C++ with libs for that. I tried doing it in Node using cluster and let it spread through our whole cluster of nodes (funny how it plays on words) and performance was worse obv. but not that bad, it was about 2x slower, but with that I was able to write code and test it in hours instead of days, and it didn't matter to me if a program that was going to take 2 hours was now taking 4 as I was going home anyway and check it out next morning ¯\_(ツ)_/¯. Plus, my experiment had a REST API for free (well, like 8 lines of code) and when I showed that to them they were :O.
var a = new Queue();
var b = new Queue();
a.read(() => some callback);
b.read(() => some callback);
And code continues here without blocking.
To be honest, I think you could do the same in any (slightly) modern language.
https://www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/tu...
(of course it was possible before)
This example sets up two timers which will regularly push events in the queue, while two threads processes these events. It's from before c++ had lambdas - nowadays it can be written much more tersely.
I'll try to write a short example after when I'm not on my phone - it should be reduceable to 5-6 lines of setup at most
#include <boost/asio.hpp>
#include <atomic>
#include <iostream>
#include <thread>
int main() {
boost::asio::io_context io;
using work_guard_type = boost::asio::executor_work_guard<boost::asio::io_context::executor_type>;
work_guard_type work_guard(io.get_executor());
std::vector<std::jthread> t;
for(int i = 0; i < 10; i++)
t.emplace_back(std::jthread([&io] { io.run(); }));
std::atomic_int counter{};
while(counter < 100)
io.post([&] {
std::cerr << std::this_thread::get_id() << '\n';
counter++;
});
io.stop();
return 0;
}Depends on which implementation you're talking about. One of the stated goals of the Go project is that there must not be just one implementation. gc does not, but tinygo depends on llvm. gccgo depends on gcc.
Without that clarity, you end up with incompatible, spec-compliant implementations. This is a disaster, and so below a certain point relative to language complexity a shorter spec is simply wrong.
So the answer to your question is "not that I know of", but that's not evidence that a one-page spec is workable after all.
The Go team officially maintains two implementations, gc and gccgo, to ensure that implementation doesn't end up ruling over the specification as has happened in other languages. There are also other third party implementations, including tinygo.
Which spec-induced incompatibilities across these implementations are you referring to?
Woe unto those who do not follow the idiosyncrasies of how golang handles versioning, package management, tooling etc.
if this is really your most important part of a language I would whole-heartedly recommend looking at rust which has been a breathe of fresh air in terms of package management and tooling.
Seriously though, Rust team needs to have its Come To Jesus moment about not piping shit off the internet to sh.
My company deploys many Rust (web) applications to production and I've not experienced these issues with cargo
To start off with, please read this: https://lwn.net/Articles/889924/
As you start having to worry about deploying to 10^5 scale machines you want your mindset to be more like the longtime kernel development folks. You want minimal dependencies. You want to build with minimal pulls to the internet. You want repeatable static binaries built from a source tree you can reasonably audit.
At that scale of deployment your "business risk" goes up significantly.
Rust's approach to tooling/packages looks very similar to the Node.js ecosystem. It's a total free for all and dependencies are managed really poorly (in the sense that they grow in number completely out of control).
I do wonder what the solution is though because Go has the same problem as far as I can tell. It has a bigger stdlib so there's less need to reach out. But when you do, you're in the same state with lots of tiny packages that do 1 thing well.
I don't have much experience outside of Go/Rust/Node so I can point to maybe a few languages which I can see being the opposite:
C++ has a large stdlib, and boost. With C++ managing dependencies is your problem so I guess it's easier to have few megapackages.
Python feels the same for mathematical computing. Numpy, TF, Pandas are all huge. Conda is an event bigger bundle, again I think because using the standard pip tools is rather cumbersome and usually not what people want to do
So as far as I can see, it's either easy to share code for others to use, in which case you get dependency hell, or you make it hard to share dependencies, but you have larger more refined ones as a result.
Hard to say what's better
And if you're still running something like CentOS 7, everyone just tells you to go download a Snap for it. eff that.
I suppose you can define feature flags in your Cargo.toml but that's about it.
The fact that you can cross compile a win10 .exe on a raspberry pi with a single command as easy as "GOARCH=amd64 GOOS=win go build ." it's very powerful and I don't know a single language that does it out of the box like go does.
Go tooling is one of the strongest point of Go you have all of that built-in:
- compilation
- testing
- benchamark
- dep managment
zig build -Dtarget=x86_64-windows
I've written a fair amount of Go and I am not bothered at all by the general ergonomics of the language. Some of the issues the author points out (like accidentally copying structs with embedded synchronization primitives) are caught by linters which you should be using regardless of what language you are coding in.
Edit: I dabble with Rust from time to time as well.
For example, I hate python as a language (spaces syntax? Self,self,self, lack of static type, etc) but I've been using it lately and it's nice using it due to libraries, community,etc.
I'd say if you run into someone who says they're experienced with X or love X etc., but they can't give criticism of it, they're most likely either not that experienced with X, or fanboys.
Take Haskell. If I were a Haskell novice, and tried to do everything procedurally with do notation, and then complained that Haskell had all these problems, would that make Haskell a bad language? No. Would it mean that all Haskell programmers were telling themselves lies in order to keep using it? No. It would mean that I was using it badly.
If I were a brand new programmer, and took up Java, I might well complain about "public static void main(String[] args)". But an experienced programmer would brush that off, telling me that that's just syntax - there are real problems with Java, but that isn't one.
So this particular statement, that senior and more experienced devs ignore more and more problems, isn't proof of anything. It's particularly not proof that developers have to keep lying to themselves in order to keep using Go. (They may, but this isn't proof.)
I've been coding professionally since the 90s. The last project I worked on was in Go. It was pleasant and surprisingly productive. A few warts, like the parent said, but all told it was a great experience. I'm now working on a Typescript project. It is painful. Typescript itself is quite nice, but the deep problems with the rest of the ecosystem have not gone unnoticed.
Go made prototyping our product easy. We have relatively rarely needed Go expertise on the team to make the product better, when we did it was for the exotic iOS environment (not normal iOS, the Network Extension).
It’s far from perfect (and I really should write an experience report about all the times Go has let us down!), but it gives us less trouble than Node and Swift do, for our relatively minor uses of those languages. I don’t think any other language would have been a better choice, even putting our expertise aside.
On the whole I would say you don’t need Go experts on your team to build products in the language.
"There's two types of languages, languages people complain about and languages no one uses"
Is author being serious about all people who write Go for production would be waking up at 3am to fix issues? If this is happening, software engineers are being abused, enough said
And not recommending C/C++ because it is easy to shoot yourself in the foot, apparently the devs enter hospitals so many times when memory-related vulnerabilities happen in Windows/Linux/Android/MacOs/iOS and Chrome/Safari/Firefox
people should stop idolizing programming languages
I will take all the footguns in the world for that ability. Plus in general the tooling is just amazing. I know there is other Unix tooling out there but I'm sorry, they kinda suck and are terrible to use. They're for the type of person who can use vim with their eyes closed and unfortunately that's not me.
The other footguns he mentioned? Valid. A linter will catch a chunk of them though. I just generally don't think they matter half the time. I think of it like the iPhone. It's a pleasure to use like 80% of the time maybe 85% and 10% it feels hacky and terrible to get something working and 5% you just can't or shouldn't do it.
So for the 80% use case (backend crud apps or backend web apis) I'd take Go every time. Plus it compiles almost instantly versus multi minute turnaround times in a lot of other languages.
Spring Boot is not Java. You can debug Java just as easily. On the upside, you can probably get the same stuff done in probably one third less code + use the vast ecosystem. On the downside, compilation may be a bit slower and you don't get a nice little executable out of the box.
I wrote a tool in Golang and while it did everything as advertised, the boilerplate just bogged me down. Any new changes is just so much more LOC compared to Java (or Python or Rust...). To be clear, I would prefer Java or Rust compared to Python any day. Either way, I just don't want to write so much verbose code anymore.
Yes, Spring Boot is very popular, but there is a whole world of Java outside Spring, even if you confine yourself to just web apps (which is far from the only domain in which Java is used).
I led a team that wrote an accounting & reporting software for a specific technical problem. We had an architect who only knew Spring, and hasn't been out there pretty much in a decade.
I clashed severely with him, to the point where he made an arrogant argument about something he was clearly wrong with. He said "I bet my salary that you're wrong" in front of people. I bet mine too, and the following day I demonstrated that he was wrong. I gave him my bank account (it was pay day). It was a hard bet to lose, so I said he didn't need to pay it, and I asked him to be removed from the team (because he showed that he wouldn't learn).
We went on to build the system without Spring, without TomCat and with more freedom to write SAL where an ORM couldn't solve the problem better.
Interestingly, we used grpc between the front-end and the backend JVM microservices.
As other people say, check out projects in GitHub. Look at what new Java, Kotlin, Scala projects look like.
There's a lot of interesting stuff in JVM-land that don't touch Spring.
Interestingly on the software that we wrote, compute was the biggest bottleneck. I rewrote some parts in Rust, but ended up canning the work because nobody else would be able or willing to touch the code.
However, with respect to the points about Go as a prototyping/starter language, there is not better language to start writing a project with in my opinion. Lots of languages have big communities of packages of various levels of maintenance but almost no other language has a standard library that is as usable as Go with the same guarantees between versions. I think its the biggest downfall of all these new languages like Rust/Zig/Hare etc, they all go for these minuscule standard libraries. I have no wish to start a project with any of them as all of them would involve I hunt down the 'best' http library and the 'best' async library and weigh their upsides and downsides, so I just reach for go and crack out the code I need to get it done, nothing else lets me do that half as easily, maintenance may be harder but at least I'm probably going to spend less time maintaining the list of packages that are still usable.
With Java and C# there's a lot of other fuss involved with figuring out deployment. The code, build, test cycle is also much slower in C#/.NET ime. That's starting to improve a bit with the newer dotnet, but still feels behind other everything but the kitchen sink frameworks like Rails, which I would more compare C# to since C# without .NET/dotnet is uncommon.
> fuss involved
The commands are almost the same from the go tooling, dotnet build , dotnet run, dotnet format, dotnet test…
Anything outside of hellow world for a web sevice in go, you will use a third party library while in .NET its given to you.
C# also had pretty middling linux support the last time i worked with dotnet. and java tooling is not as out of the box as go is where I have to know is gradle good or still used or should i just use maven. Its all part of me not wanting to have to choose the best dependencies for my project and being handed for the most part good enough dependencies by the language standard library.
Another thing to appreciate in go is it produces good enough binaries and produces them very very quickly -- i've worked on several massive go projects and the build/test turnaround time is quite good.
Go is good for onboarding new devs because it's simple. Our existing PHP devs were taught Go and it was painless. Jumping from PHP straight into Rust would be pretty painful, I think. Finding Rust devs isn't easy in our town.
But I agree with most of the points. You got to be very careful when writing in Go, because there are many gotchas. So many gotchas that I had to write a large document describing all the conventions and rules to remember to write bug-free (hopefully) Go. This "Go with conventions" reminds of the days when you'd try to simulate OOP in C - it works but error-prone.
Many of the problems are caught early with good unit and integration testing, however, just like with scripting languages. In my experience, most of the bugs in production in our Go microservices come from logical mistakes, not from misusing Go.
Rich Hickey gave an all time classic presentation about this seemingly nitpicky but important distinction https://www.youtube.com/watch?v=SxdOUGdseq4
To those who do use Go: someone can call your baby ugly and you can still love it.
It always plays out that way though.
Clearly, the people flagging it care, whether because they disagree with your assessment of it providing legitimate criticism and unique points in both cases, or because they think some other quality it has outweighs that in assessing it against the bar for belonging on HN.
> Some call it "inflammatory" but it has scientific and critical value nonetheless.
Value is subjective and people clearly disagree with you on this point.
"Users [flag] post as breaking the guidelines or otherwise not belonging on HN."
The fact that a small subset of the Go community is triggered by this post and can't behave in a civil manner isn't a reason to flag it.
Honestly I just hope @dang nukes this thread from orbit, and maybe it'll be posted again and get a cooler reaction from everybody.
Eh, unless there's evidence a submission is genuinely shitposting--i.e. trolling the community by being intentionally divisive while not actually believing what they're saying--or is outright offensive, I really don't agree.
Writing provocative critiques, like this, has been a standard in the tech world for as long as I can remember. If the rule is "don't post anything that runs the risk of upsetting someone", we'd have to ban half the leaders in the community, including Linus Torvalds.
That it isn't the most inflammatory commentary (even if one agrees with that, which is, of course, subjective) in the tech community doesn't mean that people assess that it “gratifies one's intellectual curiosity.” The latter, not the former, is the criteria for being on-topic at HN.
Naturally, people will disagree with what is within and outside of this boundary, and use flags or not accordingly.
So the worse the (Go) community behaves, the more careful authors have to be?
The post is not explicitly flagged but nowhere to be seen in HN first pages. Take that as a hint.
It's been almost 24 hours since it was posted, a majority of posts are buried by then. The post seems to have followed a fairly normal lifecycle.
I am not a huge fan of the tone of the OP either, but it does have substantive content and discusses genuine issues (though it probably is not the best way to trigger discussion of those issues), some of which have bitten people at my workplace (dealing with bugs in two different very popular products written in Go, the kind of bugs that are simply not possible in other languages) in just the last week.
It would be interesting to see the response to a less inflammatory article discussing the same issues — I have noticed in general that criticism of Go is often badly received around here regardless of its tone.
No. Here's a post with much less upvotes, much less comments still on page 2: https://news.ycombinator.com/item?id=31205139
There are many other examples if you care to compare.
Meanwhile this post is nowhere to be seen because most conversation here is not up to HN standards.
Flagging is detrimental to post ranking. And rightfully so.
> Flagging is detrimental to post ranking. And rightfully so.
As I understand it, flagging is to get moderator attention to posts that don't follow the HN guidelines. It's not the general way to punish posts that upset you, that's called downvoting. Treating flags as a "stronger downvote" basically makes the very concept of flagging worthless.
There are multiple posts older than this one that are listed within the first 9 pages, some of them 2 days older even. While this one isn't.
Take a guess why :)
The flag was removed by moderators, but by then the algorithm already did its thing.
> Take that as a hint.
As a hint that sometimes you should do you research before making claims? Agreed.
> I read the content as lacking maturity, emotionally loaded
The article is perfectly fine, but what I gather from the reactions of Go fans is that they are lacking in maturity and become too emotional if their toy is criticized.
Something the author explicitly mentioned in his article. You should read it.
The timing is a coincidence.
Unless you consider this post either spam or off topic (and I personally don't believe it falls into either category), then flagging the post is not in keeping with my understanding of the site guidelines.
Just don't upvote it.
HN is a powder keg of emotions about programming languages, and fasterthanlime's posts tend to be... shall we say, "sparky"
I currently have a ~80Ksloc codebase in Go.
I like python. I dislike go.
I still hold the position that scaling a codebase is easier in go than in python.
* Goroutines + channels are generally easier for someone besides the author to reason about than their python equivalents, leading to a more consistency and better boundary definition.
* Python has a lot of magic in it - and that stuff is really cool, powerful and fun - but.. it also makes it incredibly easy to write stuff that works well together until it breaks in some wierd corner of a seemingly unrelated object somewhere.
* As python codebases grow, duck-typing becomes much less pleasant - you start seeing a bunch of `if isinstance(...)` in the code and eventually someone will come along and start implementing parts of a type system on top of the python code that exists (see also the previous point).
Those are the primary reasons for me, but there's also just a certain lack of friction in the go codebase (compared to python anyway) that I can't really describe well.
At the moment I am maintaining a C# codebase which doesn't feel maintainable at all, and I don't think C# is the only problem there.
Just this week I've been dealing with a random crash when we run `terraform plan` which turned out to be a data race in Terraform itself. I honestly can't understand why something like Terraform, dealing with critical infrastructure, secrets, etc, would be built in a language like Go, apart from that all the other ops-adjacent stuff was being built in Go. The discussion in TFA about not selecting your tools solely based on what other companies are using is cogent.
"It may well be that Go is not adequate for production services unless"
Who is exactly the author to tell us to not use a language that was proven to be just fine and successful. What actually OP shipped in production to be able to make such claim? Does it means also that Java / C# / Python, Ruby ect have the same fate since they're not up to part with Rust?
Kubernetes is everywhere and is built 100% in Go, it solves real complex issues, so was Go the wrong choice for it?
You can find many tools built in Go that are used by pretty much every compagnies now days, was Go the wrong language for those tools as well? Kuberentes, Docker, Grafana, Prometheus, esbuild etc ... the list is actually long.
Yes Go is not a perfect language, but saying that you should not use it production where the last 10years has shown that it deliver value is wrong.
Besides that, you do realize that your comment is a perfect embodiment of "Others use it, so it must be good for us too", which is the very first "lie" that he is describing?
At what cost? How many man-hours did it take? How many bugs could have been avoided? How much faster could it be done with other languages? Most importantly: if Google stops backing its development, who else could replace it?
Redis/SQLite are written in C, and it is also used by millions of people. They are also not known to be bad or full of bugs. You don't see that being used as an argument to claim that C is an "adequate" language for most people nowadays.
And if you did see anyone using that argument, we probably would say that this is just a lie they tell themselves to keep using C.
Same question can be asked when Rust or C/C++ are being used to write k8s
> And if you did see anyone using that argument, we probably would say that this is just a lie they tell themselves to keep using C.
A bad programmer is a bad programmer. If all you have is a hammer, everything looks like a nail
People who did read this article should be aware of the shortcomings of Golang by now, and some of them will still be using it, and I am very sure of that
b) please read the article before complaining
What kinda gets me from this genre of post, is that it sounds like writing good software in any language besides Rust is impossible, or even impractical. Like 10 years ago it was impractical for anyone to enjoy writing good software. (Or maybe Haskell or Ocaml would be acceptable?)
I've written a fair bit of Go, lots of Python and C, lots of bash and posix sh ... and I've written good and reliable-for-purpose software in all these languages, and I've enjoyed it much of the time. (In college I also wrote a chess playing engine in java, a malloc implementation and a trivial unix-like OS kernel with fork and threads in C of course, also a mips and z80 cpu implementation in verilog for fpga ... does that make me a "blub" programmer?)
Is it really an unacceptable flaw to have a program that only works with printable paths, and only makes sense to run on one's own files? Or which runs fine on both unix and windows, but doesn't do anything with file permissions? Or which only operates on file permissions on unix, but only runs on our servers and VMs?
I like high quality software, I really do. But I think that people writing these blog posts want something more, something impossibly pure. They want a messiah, and if everyone just believed, the world would be perfect. I like high quality software which has been possible and practical for 20+ years.
The fact is that (as the author pointed out) Golang is missing basic language features that other languages have adopted since the creation of C that eliminate whole classes of errors, and it's pretty easy to accidentally do the wrong thing as a result.
Nil pointer exceptions, for example, don't have to exist anymore.. and yet they do in Go because they couldn't be bothered to add sum types. Its type system is barely a step above a dynamic language. You have to write the same imperative looping code over and over because Rob Pike would rather just use a for loop than something mildly expressive like map or filter (https://github.com/robpike/filter). Every function that does meaningful work is littered with if err != nil { return err }. Etc.
It is easy to write code in Go, but IMO it's not easy to write and maintain a production grade system with any amount of complexity in Go because you _have_ to be careful, you can't encode invariants explicitly and let the compiler find problems for you, and you have to repeat yourself so often.
'Folks who develop an allergic reaction to "big balls of mutable state without sum types" tend to gravitate towards languages that gives them control over mutability, lifetimes, and lets them build abstractions.'
This mutability argument is present throughout the article. Seems like nothing sans Rust or niche functional languages is enough.
> Nil pointer exceptions, for example, don't have to exist anymore..
The language most notorious for those is Java due to almost everything being passed via a nullable reference. When everything can be nullable, how can you know where to check for it? Go addresses this to an extent by explicitly separating pointers from values. Values are the default and cannot be nil, so the opportunity for null dereferences is greatly diminished. It's not a perfect solution, but it's not nothing either.
> and yet they do in Go because they couldn't be bothered to add sum types.
Damn those lazy Go devs!
> Its type system is barely a step above a dynamic language.
Turns out even a basic type system is a huge improvement over none. Just being able to restrict values to concrete types goes a long way.
> You have to write the same imperative looping code over and over because Rob Pike would rather just use a for loop than something mildly expressive like map or filter (https://github.com/robpike/filter).
There are arguments to be made either way, but I definitely agree generics (along with iterators) should have been there since day 1.
> Every function that does meaningful work is littered with if err != nil { return err }.
One big positive of this that I don't see in other languages is every `return` in a function must be on the start of a line. That is, every single exit path of a function is easily findable by visually scanning the start of each line for `return`.
> you can't encode invariants explicitly
I'm curious, are there any limitations here besides enums/sum types?
I think what we are seeing here is that the expectations are growing and people demand more from their tools.
This is a good thing, even if some languages cannot keep up with the higher standards and fall behind.
A little?
"[...] but I remember fondly the time an audience member asked the Go team "why did you choose to ignore any research about type systems since the 1970s"?"
"Unless you're out for confirmation bias, that whole article is a very compelling argument against using Go for that specific problem."
"[...] and tooling that would make C developers jealous, if they bothered looking outside their bubble."
"[...] and most importantly, you adopted a language that happened by accident."
"Evidently, the Go team didn't want to design a language. [...] "And so they didn't. They didn't design a language. It sorta just "happened"."
"And breaking down an argument to its smallest pieces, rebutting them one by one, is a self-defense tactic used by those who cannot afford to adjust their position in the slightest. "
"We've reached the fifth stage of grief: acceptance."
"Because it has been decided that abstractions are for academics and fools [...]"
> [...] but presented very honest and accurate information about a language that the author clearly has experience in.
It's biased flamebait, just like the original article.
> It seems like a lot of the people complaining in the comments didn't actually read the article. The author even explains how those who have bought in to Go may not want to hear what they're saying and it's right. No one wants to hear that their baby is ugly but sometimes it's the truth.
>
> To those who do use Go: someone can call your baby ugly and you can still love it.
Sure, I'd love to read such a well-considered article. Please link any you know of below.
Here's an exemplary article that managed to criticize Go without evoking a flamewar ([1]): https://gitlab.com/esr/reposurgeon/-/blob/master/GoNotes.ado...
I pose that if you present valid arguments in a reasonable, intellectually-curious way, people will respond positively. But if you set out on a holy war instead, well...
I think Amos has presented more proof than I can on the topic of just how Linux-influenced Go is. And I think it is fine for the majority of Go users because the majority users of Go are building server apps or Linux CLIs.
Amos has spent some time building cross-platform desktop systems with Go for itch.io and I think I'm seeing some of the same things they are in that scenario.
I think this is a reasonable article. I didn't notice anything too flame-y myself but if for you Amos gets flame-y at any point I think that's worth ignoring because there does seem to be something up with Go in cross-platform applications. Amos does have good experience here. (Go look at itchi.io's github like wharf [2] or butler [3] where they are/were the main contributor.)
I like Go a lot and for most things I'd keep using it still. Just sharing some observations.
[0] https://github.com/multiprocessio/datastation
[1] https://github.com/multiprocessio/dsq
So while this article didn't really convince me of anything, I have to admit I did learn a thing or two (or five). Kudos to the author, who is a fantastic writer and communicator. Even if you aren't neck deep in the language wars, it's worth reading.
Working in Python, I constantly run into errors that static typing could have caught. Working in Rust, I feel like I'm constantly fighting the borrow checker. Maybe a more experienced Rust programmer would feel differently, but one of the strengths of Go is that it's relatively easy to learn
It can do this while also being easier to learn than go too (designed so students could learn it and implement it too).
The only reason go is popular is because google put thousands of man years and countless millions of dollars into it.
I generally heavily preference scripting/interpreted languages over compiled languages, because compiled languages have a bevy of stuff that qualifies as "getting in my way". Go is refreshingly not like that, and therefore is the primary compiled language I use these days, and largely the primary language I use at all. Then again, I'm not an SWE. I'm a SysOps/SRE turned PM-T. So it's unlikely I would ever dig deeply enough to care about the particulars of a language's design other than knowing which problems it is best at and which problems a better choice can be made to solve.
Both this article and his previous article, I've read thoroughly. I understand and appreciate the criticism. But fundamentally there's seems to be an implicit assumption within both articles that other developers who use Go don't already know (most of) these things. Anyone who uses Go long enough will eventually run into some of its shortcomings. I'm not sure exactly why it rubs me the wrong way, but it feels like there's something subtlety insulting in the way the author presents his arguments. Like if we weren't all stupid rubes we'd just use whatever his preferred language is (Rust, presumably?).
The way people get zealoted about languages these days (especially some pretty terrible ones for getting things done) tempts me to just go back to writing Perl. In all the languages I've used over the years, I've probably got more raw work done in Perl than anything else, and it's still near and dear to my heart, despite being objectively a badly designed language.
I appreciate the nil method receivers, the zero value meanings, the types that are actually hidden pointers, but if you're not coming from C, a lot of it sounds inconsistent and counter-intuitive. And I'm not sure there's a way to justify it to programmers who have grown up on better, safer, clearer abstractions, even with the potential performance gains.
I think there are two reasons.
First, languages live or die by their ecosystems. If you love a language, it's very much in your interest for other people to use it, so they write code that you can use.
Second, most programming happens at work. Most people don't get to choose what programming language they write at work: they're locked into whatever choice somebody else made. The more popular your language is, the more likely that choice is to be made in your favor (or alternatively, the easier it'll be to find a job where that choice has been made in your favor). If I want to write Idris at work, well, tough luck.
Both of these are largely zero-sum, which makes things worse. It's not 100% zero-sum, in that if you manage to light a passion for <language> in somebody they might write code (in that language) that might not have been written at all. But mostly language evangelism won't change the total number of programming hours per year (or the number of available programming jobs), so all that's left is to fight over who gets how much.
So if you care a lot about which programming language you write, I don't think it's odd to come at it from combative angle: it's a fight! Some will lose and some will win, so you better try and be one of the winners.
Because the market identifies job suitability by years of experience with a particular tool, so the popularity (in the market) of tooling a programmer has experience with is intimately tied to their job opportunities and financial prospects.
If you are an “X programmer“, attacks on X are attacks on your livelihood. Conversely, if there is an abundance of Y programmers, convincing people that X, which far fewer are proficient in but where you are one of them, is superior for some high-value domain, is enhancing your livelihood.
You’ve seen informercials exaggerating the negatives of alternatives while hyping a product in order to sell it? Well, while it's framed as being about tooling that's one step removed, that's basically what programmers are doing in language flamewars.
Without analyzing the rest of the post (which I read without having the skill to fully comprehend), this stuck out to me.
Perhaps the language behaves one way and not another by design? It's abstraction. By using a Struct, I'm telling the function "don't worry what is in this one box of data, just take it and process it as best you can" while defining two inputs to a function says "I demand that two pieces of data be present".
I don't think it's an inconsistency to see this behavior.
I really think that if we follow the "zero values by default" philosophy, it wouldn't be shocking for Go to accept omitting function arguments and just zero them.
The fact that it doesn't, and insists on you passing the right number of arguments, makes the point that you can't in fact, just afford to zero out everything that isn't explicitly specified.
> Go not letting you do operator overloading, harking back to the Java days where a == b isn't the same as a.equals(b)
Does this guy really not understand that in a mutable language, (eq ...) is not the same as (equalp ...) and should never be confused with it?
Also: operator overloading is the spawn of Satan.
I used to agree with you, because of the abuse of operator overloading like iostreams and making DSLs with it. D does support it, but in a way that discourages non-arithmetic uses (such as it won't allow < <= >= > to be overloaded separately).
Operator overloading allows for things like complex numbers, arbitrary precision numeric types, etc., to be done with a library module.
Why is this desirable vs. implementing those types in the language itself?
I think we've gone down a weird path where implementing things "in userspace" is seen as an inherent good -- why?
That's a very good question.
1. It's simpler. In D, we transitioned from a builtin complex type to a library type. It was a happy experience. The simpler the compiler, the easier it is to deal with.
2. Most any programmer can create a library type. Relatively few can modify a compiler to add a new type.
3. It takes the pressure off the compiler team to develop more arithmetic types.
4. Users can add arithmetic types without needing anybody's consent, and can do it right now.
5. It tests and verifies the metaprogramming abilities of the language.
There are always going to be more numeric-like types (int, float, complex, bigint, rational, symbolic-comp types etc.). Numeric vectors that act like numbers are useful (see: numpy), and trying to stick those all into the language is far harder than allowing extensibility.
But at around the end it asks this (paraphrasing): Should a language have complex numbers implemented natively? Should it have numbers module n implemented natively? What about intervals, or rational numbers?
I also dislike the ridiculous overloading of cryptic symbols, but neither extreme is good. Not allowing overloading will give you BigDecimal.of(3).add(BigDecimal.ONE).divide… , while allowing it unrestricted will give you things like msg #!! something. But perhaps a sane middle ground is to allow only basic operators (+,-,*,/) to be overloadable.
In languages with operator overloading this just isn't a concern at all because == does the obvious thing
And what is the obvious thing? It varies wildly among languages, even within C++ itself.
Every time I have to write `BigDecimal sum = one.add(two).add(three).minus(four)` I cry a little. This is effectively no different from operator overloading and it's what special number implementations in all languages without operator overloading seem to end up doing out of necessity.
It's necessary for "powerful" generic programming. In fact, the entire reason user-defined data types can be easily used in C++ libraries like CGAL, Eigen, fmt, Kokkos, ranges-v3, etc, is because of operator overloading.
It is. And I'm glad Zig has none of that. `==` only works for primitives and you will never be confused.
For example the headline "Mom smokes, so it's probably okay" and then 8 paragraphs of lead-up to the argument that the Go standard library has an inelegant IP type. Afterwards the author equates people using custom types to "build a whole new type system".
"Evidently, the Go team didn't want to design a language." is also pretty insulting if you consider how much the Go team wrote and talked about why they designed things in this way.
And to make a lot of weird arguments work, the author then refuses to treat any cgo feature as part of the Go language ecosystem. That's the point where I was convinced that the author wrote the summary before researching the arguments.
It immediately follows up with interesting discussion about how it's acceptable for a team of experts to use Go. I think he's wrong, Go is perfectly powerful in the hands of novices. And I actually think a team of experts like Tailscale would probably be much better served by choosing Rust.
But that's all besides the point. The article is excellent, fits our forum and is a great basis for discussion. You are not using the flagging feature as it's intended to be used.
There's something that I think is important to mention: there's a difference between theorical ways to make bugs and bugs that happen in reality. The author spends more time talking about theorical ways to make bugs than bugs that happen in reality. I'm all for the "we should be using good tools", but there's also a point where you shouldn't use a tool just to use it.
An example of that: at work we have a very huge codebase that's a pile of C++, C#, JS and TS. The codebase is more than 20 years old. Typescript is a relatively recent addition. I've seen part of the adoption, and talked a lot with the people that adopted it. And the conclusion is that it didn't really reduced the errors we have in production. We already have processes to cover for that: being careful during development, pair programming, code reviews, automated tests, manual tests. However, it made the development way easier, since Typescript has a better IDE support. But it didn't lead to use reducing our processes.
A lot of the article seems to be rationalizing bad experiences with Go due to bug happening in production. I don't want to reject the criticism entirely, but I think the language isn't the only thing you can blame here. And my own experience is that processes matters more than the language when try to write good code. It seems like the author has a different experience compared to mine, and I don't want to fall into the fallacy of "it's possible to write bad code in every language". But I think the author should recognize that people can have widely different experiences, and that there isn't always someone that's right and someone that's wrong.
I've read lots of thing about Typescript adoption and lots of people/companies mention a reduction of bugs in production. This didn't happen for us. That doesn't mean they're wrong, and that also doesn't mean that Typescript is useless for reducing bugs in production. It means we have different experiences. I think it would be more productive to talk about our experiences, and try to find out why they are so different. This way we could learn a bit more about software engineering, and develop more empathy for each others.
On a large enough (time)scale, no there isn't.
But also, all this is rooted in years of running and/or watching someone run Go in production. These bugs aren't theoretical, they cause incidents and outages, and occasionally wake me up. I don't like being woken up.
Edit: I'm really shocked by the downvotes. Can someone explain why my comment is worthy of being downvoted?
But the OP, in either article, zeroes in on narrow language/STL design choices and never zooms out to consider the ecosystem and how Go works in an actual software engineering team. Pardon the vulgar analogy, but this is like zooming in on a dog's asshole and going "what the fuck is this? It looks gross and shit comes out" without considering that it is an integral part of a well-functioning whole.
It all comes down to misunderstanding each other's positions.
Based on the article, I doubt it.
That is a super weak move.
I think all the arguments against Go in this article are fair and I think you can write articles just as long about any programming language of your choosing. It's very strange to see the level professional programmers will stoop to if their favourite tool gets criticised.
The fanboys of programming languages are the worst part of a language community. The "why not rewrite it in Rust" and the "you're golanging it wrong" crowds only drive people away from their respective languages.
Also it's not a ,,new'' way to discourage reasoned discussions, it was already like that when you decided to join HN in 2016.
But if I would be censored, that's OK, it happened multiple times before, but I still prefer it compared to other web forums, because I deserved it.
This is in response to that post from yesterday. It's worth flagging this one, or else it's going to be an infinite loop of knee jerk reactions. We can do without these.
If users didn't flag these, HN's front page would largely consist of meta flamebait follow-ups.
Here's the principle: curiosity withers under repetition and fries under indignation; also, meta is the crack of internet forums. Repeat+indignant+meta is definitely worth flagging.
You and I know perfectly well that some kind of tribal loyalty around loyalty on a programming language (they all suck) is an IQ test.
I’ll look the other way on Siebel committing outright fraud, and you let these idiots argue about go vs Rust without displaying any bias.
Everyone knows that Thompson invented UTF-8. And I’m smarter than Pike, who was always a pity hire.
As a Rust jihadist myself ... yeah I can't disagree with this description. Even I get sick of all the Rust chatter/evangelism on HN sometimes. I love it and it's my preferred tool, but I wouldn't mind hearing about some alternative topics at least some of the time.
(As for the article: I wrote Go for several years professionally, and what he says, well - rem acu tetigit. I know a lot of people on that former team who were converted to Rust by the experience of writing Go. And then I know others, with a natural propensity to cultishness, who just responded the way so many people are responding in this thread.)
It's one of those phrasoids which is permanently stuck in my head from Latin classes when I was a teenager, but I might've been wrong in thinking it was one of the more widely-recognised ones.
[0] 'You' is implied by the second-person verb ending on 'tetigit', 'with' is implied by the ablative case ending on 'acu' (the 'ablative of means'), while 'the' and 'a' are filled in altogether since Latin has no concept of definite/indefinite articles.
The multidisciplinary lab I was in had a "Go or Rust" dilemma. As the language guy, I was asked for my opinion.
I had no experience with Go, and a small bad one with Rust: there was weird stuff going on with pointers, and even seemingly simple programs would fail to compile due to a less than happy borrow checker. Working with Rust required a heavy learning investment. On the other hand, Go looked simple, with amazing goroutine, fast compilation time (great for the code/compile loop), and backed by a big company, which is reassuring for then still young languages.
As I could not form my opinion on significant coding experience, I looked at the principles. It appeared to me that Rust design was guided by language principles, whereas Go was guided by system/engineering requirements. For example, Rust had generic and sum types, which Go lacked. Rust error are "naturally" dealt with using sum types, whereas Go relied on error codes. Go had that "operating system flavor", with features usually closed to the OS directly implemented in the language (goroutines, which are great). Rust had that "things are complicated flavor", in both the language (borrow checker, several kind of pointers), and the library (several kind of string?!) which at first is of-putting.
Being a language guy, I argued for Rust's good use of language principles over Go's more practical approach. My bet was that Rust usability would improve. A mix of Java/Scala was chosen. I now code in C++...
--- --- ---
A note on simplicity. Simplicity is a lie. Things (not only programming languages) are not simple, they are messy, hard. Truly hard. In my experience, simplicity claims are usually superficial, and show their limits when going a bit deeper. As programmers, we like to have simple, generic, abstracted constructs, because it makes our life easier. But the world is not generic; it is full of special cases, corner cases, situations that do not fall in a nice hierarchy, which is its beauty. It took me too long to "accept" this beautiful complexity, and to stop fighting it. If you are still looking for simplicity, maybe check that you are not fighting the world's complexity.
I'm not proficient nor use at $DAY_JOB C, Go or Rust. However, I wanted to write an Emacs dynamic module and those were my main choices AFAIK.
I've picked Go to write a grunt work module (fetching JSON, parsing it, transform it, send back to Emacs) and it's been smooth sailing.
I then found the benefits of writing client glue code between Minikube, direct K8s and Docker and Emacs a breeze.
What lie am I telling myself?
I see this article being an address to these criticisms (which are not limited to this forum, I've seen similar arguments on twitter, etc.).
Pointing out shortcomings isn't really impolite or unfair. The author specifically mentioned shortcomings of Rust as well.
It's also more accurate to say that Rust wasn't designed / was an accident since it was technically a side project of one guy at Mozilla.
One can plainly be a good programmer and yet create a bad language. I'm sure you are well-informed enough to analyse his argument on its merits, even if it were written by a gang of monkeys with typewriters.
That's one of the most mild and inoffensive criticisms I've ever read on the internet. You can't seriously be indignant about that.
Of note: the last time in question was yesterday, the flagging and ad-hominems pretty obviously made lime's cupeth runneth over.
I have no particular affinity for Go or Rust. As an outsider this is not how I’ve ever perceived the Rust community, because the most present and visible parts of the Rust community were people showing up and complaining that Project X didn’t use their preferred silver bullet
It's also way too hugboxxy and tbh that feels just as toxic some times as the alternative. Positivity to the point where it's obviously fake and forced and insincere.
Edit: howinteresting I can't reply to your comment but you are proving my point. At least you people are being more honest that it is intended to exclude a bunch of people.
(FWIW, I appreciate your open-mindedness. I think that's the main thing I find objectionable on (parts of) both sides of the aisle: an inability to regard those who disagree as fundamentally well-intentioned human beings who happen to hold different beliefs.)
What, you cant handle queer people existing?
I’m glad we have as many languages options as we do, and I enjoy writing a variety of languages for a variety of different projects. Sure, I have moments where I think “ugh why can’t I do X in language Y” or “C is cleaner in Y” etc etc.
I’ve found helpful people, critics, and fanatics in every language community. Don’t let that dissuade you from learning Rust (or Go, or …)
Edit: though, I suppose saying Go is not designed is also equally unnecessarily dismissive and hyperbolic.
it's kinda a shitpost
And let's face it, Portland kinda takes itself too seriously.
As for Portland taking itself too seriously, are you serious? Portland has so many quirky things that the conservative states don't have(much less putting walls up and making some people, making 2nd class citizens ((yes, voting rights)), and you say Portland is too serious? Good luck with that gaslight.
Guy makes a blog post about Go a few years ago. It gets posted here time and time again, not of his doing. A bunch of people challenge his premises, so he makes another post diving deeper into his arguments.
So what?
What's up with the literal hundreds of "Golang is amazing" posts this site has had? Is someone attempting to express dissent clearly that much of a bother?
What makes me angry about all this Go-bashing is that it might influence decision makers against moving from things like PHP or Node to Go - which I think would be an improvement, no matter how many flaws Go may or may not have.
It's just a language! Take it easy.
The guy is not "heaping on invectives" - he's not criticizing you or someone you care about. He's pointing out some flaws in a language in a sarcastic way.
"Mom smokes, so it's probably okay"
"I remember fondly the time an audience member asked the Go team "why did you choose to ignore any research about type systems since the 1970s"? (Emphasis his, not mine)
"It doesn't matter who points out that "maybe we shouldn't hit ourselves in the head with a rake repeatedly""
"Or you can be horrified, as you realize that those complex problems only exist because Go is being used."
"you adopted a language that happened by accident" (a direct criticism at Go users)
"Evidently, the Go team didn't want to design a language."
"Because it needed to be familiar to "Googlers, fresh out of school, who probably learned some Java/C/C++/Python" (Rob Pike, Lang NEXT 2014), it borrowed from all of these."
I just have up here. Do you really think a production conversation can start from this? ESR has flamed less haughtily.
"Mom smokes, so it's probably okay"
"I remember fondly the time an audience member asked the Go team "why did you choose to ignore any research about type systems since the 1970s"? (Emphasis his, not mine)
"It doesn't matter who points out that "maybe we shouldn't hit ourselves in the head with a rake repeatedly""
"Or you can be horrified, as you realize that those complex problems only exist because Go is being used."
"you adopted a language that happened by accident" (a direct criticism at Go users)
"Evidently, the Go team didn't want to design a language."
"Because it needed to be familiar to "Googlers, fresh out of school, who probably learned some Java/C/C++/Python" (Rob Pike, Lang NEXT 2014), it borrowed from all of these."
I just gave up here. Do you really think a production conversation can start from this? ESR has flamed less haughtily.
It's important for individuals to be able to do that, and it's on everyone else to make the distinction.
> Go's lack of support for immutable data — the only way to prevent something from being mutated is to only hand out copies of it, and to be very careful to not mutate it in the code that actually has access to the inner bits.
I thought everything was handed out as copies in go by default unless a pointer was being passed. So this would make it “easy” to tell whether you are mutating a object or not.
would appreciate if anyone can clear that up for me.
side notes:
since Im still a junior, not too interested in language wars and feel like everything i touch i learn something from no matter how old/new it is.
honestly, i would never have the confidence to critic this <language> in that fashion. i cant convince myself that i know enough about language design/systems to critic anything that harshly.
maybe its my shortcoming as a junior :)
Besides that, there is also the fact that slices share underlying array storage, and `append` doesn't make a copy unless there is still capacity, which means that you should be careful when working with subslices that get appended to.
I read through the example regarding the struct containing the map. I recently ran into that when working on something.
slightly meta: So how can one evaluate whether that is a good decision or bad?
Does forcing the programmer to use make explicitly for maps help prevent errors or is it tedious and better off having the zero value of map not cause errors?
There is no precise, mathematical answer to your question, since it's always contingent on many things, but one way to evaluate a “correctness” of a design choice is the old adage that a good interface is hard to misuse. If your code can handle a nil map okay, and not requiring a non-nil map brings significant ergonomics advantages, you may not need to check it. Otherwise, I personally would err on the side of caution and design an API that either doesn't give the user a choice (i.e. creates the map itself) or returns an error if they provide invalid data.
It's also hard to keep a mental model of which builtin primitives are values (copy semantics) and which are references (reference semantics).
Arrays are values. Slices are references, but might be a reference to some dynamically allocated slice or an array. Appending to a slice makes a copy _of the slice_, but which might modify the array it came from. It's not possible to tell from the type if it references an array or not.
Maps have reference semantics too. Why is it that maps and slices are the only references that don't have a pointer star to denote that?
Once you learn how to use one set of tooling well, it's not so hard to jump over to another one if you get a job in a lab that relies heavily on it. Same with languages.
Fanatical adherents of one option or another often have some ulterior motive ... like the lab who spent $2 million on that high-field NMR spectroscopy machine and is always trying to get people to use it so they can can get some co-author publications. Often the most vitriolic fights are over some technical detail or other that doesn't apply to the vast majority of use cases.
Conclusion: meh. Of course Python and C++ are the superior options, however. Plus C for things like writing low-level code for firmware etc. The fact that these are the only languages I have any experience with is entirely incidental to the truth of this claim.
I guess the confusion for me is, languages often have overlapping usecases with slightly different semantics/syntax.
How do we objectively evaluate what is better? is it industry adoption? academic praise? im sure theres an answer to that but i havent went down that rabbit hole yet
This is the most healthy take.
The first bit is correct.
The point the article makes is that, to know whether a variable is potentially mutated, you have to go look at the signature of any function called on it. E.G. you can't just look at main and "know" whether a will get mutated or not, you have to also look at the signature of `Changed`. In C, with the following code
#include <stdio.h>
struct test {
int value;
}
int main() {
struct test a = test { value: 1 };
Change(a);
printf("a.Value = %d\n", a.Value);
}
I can be 100% sure that a is never mutated, because it is passed to change value, and won't get automatically turned into a reference. Had Change be called with `&a`, then I'd know that a potentially gets mutated.In Rust, `a` would have to be declared mutable to start with, e.g.
fn main() {
let mut a = A { value: 1 };
a.Change();
println!("{:?}", a);
}
The above, I know that Change can potentially mutate a. And if a had been declared `let a`, I can be 100% sure that change cannot mutate it.The direction go took is in line with many other languages (I think C++ behaves this way, it automatically turns values into references based on function signature).
But you can make functions that have copied receivers:
type Foo struct {
bar int
}
func (f Foo) mutate() {
f.bar = 2
}
func main() {
f := Foo{bar: 1}
f.mutate()
fmt.Println(f.bar) // 1
}
> whats the advantage of creating a function of type struct a rather than creating the C equivalent of change(obj structAtype)types need to have methods in order to fulfill interfaces, that's all. (Well, and all the downstream benefits of using interfaces, such a polymorphism).
i can definitely see how that is wacky. i wonder what the upsides of being able to do that is.
really appreciate the explanation! thank you
I read articles like this, and think to myself ... some future CTO will curse my choices today. But then I feel that future CTO will curse any choices I make, and it won't be fair for her to curse my choices ... because I'm just trying to get us from default dead to default alive without running out of the small amount of money we raised.
Rust is absolutely not an option for us. We don't have the skills or time to write a Rust backend right now.
To me, as a scripter-ops kind of person, Go is useful as a Python variant. You get types, you get a huge standard library, and much of ops is network-based communication and glue code. The last job I had, I was tasked with rewriting a moderate Python lambda into golang as a POC for ease of use, and performance.
I relearned the basics of go in about two days, used google and docs copiously, but otherwise did not change the structure of the program.
The execution time was cut in half! It's an easy language to get using quickly and serves that purpose well. I wonder if the author could acknowledge the comparison to Python.
It's very unclear what this is supposed to mean. 'type Foo int' in Go creates a new 'Foo' type that can't be used as an int without casting (a̶l̶t̶h̶o̶u̶g̶h̶ ̶y̶o̶u̶ ̶c̶a̶n̶ ̶a̶s̶s̶i̶g̶n̶ ̶a̶n̶ ̶i̶n̶t̶ ̶t̶o̶ ̶i̶t̶). If you don't want to allow that, you can just wrap it in a single member struct.
You can't, in fact:
type Foo int
func main() {
var foo Foo = 1
var bar int = 2
foo = bar // cannot use bar (variable of type int) as Foo value in assignment
}
The reason you can do things like: var foo Foo = 1
is that because numeric literals in Go are not ints, they are untyped [0].Not sure why you would want a fully opaque type internally to a package anyway. A regular `type Foo Bar` declaration is sufficient to give you a `Foo` that can't be accidentally interchanged with a `Bar`.
numeric literals (mentioned by paskozdilar in a parent comment) being untyped is another footgun I hadn't even thought of including - it makes it impossible to achieve in TypeScript/Java-level enums.
But I was wondering why you would want an opaque type internally to a package. Presumably not to ensure that the value has passed some validation, since in that case you would want to encapsulate that validation logic in its own package. It's not like Go imposes a tax on packages. Packages are the unit of encapsulation in Go, just as modules are the unit of encapsulation in Rust.
You can have a public function (a constructor) returning an instance of a private type. This way you ensure it's not zero-initialised by default (you can't do "var f privateType", you need to call the constructor).
But it's annoying to do. For every type you'd ever want to protect against zero-initialisation, you'd need to declare it private in some other package, and create a constructor. Therefore, most people just don't bother and prefer to live with the increased risk of bugs.
To me it seems extremely weird to phrase this kind of criticism by saying that Go lacks proper support for newtypes, which simply isn’t true. If the author’s real complaint is with zero initialization then it would be a lot easier to understand their point if they made this explicit.
> But it's annoying to do. For every type you'd ever want to protect against zero-initialisation, you'd need to declare it private in some other package, and create a constructor.
How is this annoying? You’ll need a constructor anyway if you’re doing validation on the value. Apart from that you’re just complaining about having to make a package, but that’s really simple. I don’t see how Go would be improved by layering additional privacy mechanisms on top of the package system.
I don't get it. There's nothing particularly special about it other than it being an explicit choice (I suspect Go's developers were lazy and did whatever was easier to implement). If, instead of automatically initializing to zero, the compiler said "error: uninitialized struct field", we wouldn't be having this conversation, and we wouldn't have this class of bugs. I would consider it "fundamental" if there was an obvious benefit from this choice, but I think a more appropriate word is "arbitrary".
> In most cases you can arrange to make the zero value valid.
Valid doesn't mean correct. Corrupting the DB with zero-initialized data can be worse than crashing early due to an unitialized (nil) pointer.
> If the author’s real complaint is with zero initialization then it would be a lot easier to understand their point if they made this explicit.
They did, it's mentioned in several places. E.g.:
---
Go fails to prevent many other classes of errors: it makes it easy to accidentally copy a mutex, rendering it completely ineffective, or leaving struct fields uninitialized (or rather, initialized to their zero value), resulting in countless logic errors.
---
> Apart from that you’re just complaining about having to make a package, but that’s really simple.
In order to prohibit direct struct initialization (which can be a source of unintended bugs) and enforce using the constructors, two types "related" to each other would have to live in different packages. E.g., if a type A's method constructs a type B, you'd segregate them and keep only the constructors public. In the context of a medium-sized project, you will end up with MANY packages. Sure, it's simple to create packages, but it can becomes painful to manage once you need to understand the code. So at the end of the day, people don't do it and instead elect to be "more careful", which isn't a good method of preventing bugs.
Yes, this is what I meant. Go is explicitly designed according to the philosophy that it is beneficial overall for every type to have a default zero value. You may disagree with this, but if you find you are constantly fighting zero initialization, you should probably just use a different programming language.
>Valid doesn't mean correct.
I understand that. However, in most cases, you can arrange for zero to be a valid value. It may require a little creativity, but it's rare for it to be impossible in my experience.
As to packages, I still don't see the issue. The related types can be in subpackages of a parent package. And as you say, it's simple to create packages.
>So at the end of the day, people don't do it and instead elect to be "more careful", which isn't a good method of preventing bugs.
I don't think this choice has anything to do with the overhead of packages. Most people just don't like the style of 'bondage and discipline' coding where the type system is maximally exploited to enforce every possible invariant. Again, this is just fundamental to Go. It was designed by people who have explicitly said that they don't see value in using the type system this way.
By the way, I am no stranger to the possibilities in this space, having been paid to write Haskell code for a while. I've even done absurd things with the type system like this: https://adrummond.net/posts/cooper However, I tend to think the Go folks are right on this. It's good to have a basic type system, but there are rapidly diminishing returns on the fancier stuff.
type NonZeroInt struct {
v int
}
func (n NonZeroInt) Get() int {
return n.v
}
var n NonZeroInt // invalid!
You could of course validate on read: func (n NonZeroInt) Get() (int, bool) {
if n.v == 0 {
return 0, false
}
return n.v
}
…but that's hardly good design.You can improve on this marginally with an interface, because an interface's zero value is at least nil, which is, at least, explicit about whether there is a value or not. But it's not very elegant:
type NonZeroInt interface {
Get() int
}
type nonZeroInt struct {
v int
}
func (n nonZeroInt) Get() { return n.v }
func ToNonZeroInt(v int) (NonZeroInt, bool) {
if v == 0 {
return nil, false
}
return nonZeroInt{v}, true
}
The biggest problem with this approach is that it adds overhead. It can also force values to be heap-allocated, though I believe Go will still optimize single-word interface values to avoid this.Note that since Go interfaces use structural typing, naming the method Get() can cause issues:
type AnyInt struct {
v int
}
func (n AnyInt) Get() int {
return n.v
}
Now AnyInt fulfills the NonZeroInt interface, which is of course not something we want. So for types like these, it's a good idea to name the method explicitly: type NonZeroInt interface {
GetNonZero() int
} type NonZeroInt *int
OR
type NonZeroInt struct { v *int } // (if you want it to be fully opaque)
so that the program would panic on an attempt to do arithmetic on the nil value. That achieves the same effect as your code, but without the unnecessary interface definition and the resulting naming issues. (Though note that Go does make it possible to export an interface from a package that no-one outside the package can implement. All you need to do is add a dummy private method to the interface.)It's true of course that the pointer adds runtime overhead, but two points:
(1) Go is not really advertised as a zero cost abstraction language (in contrast to e.g. C++, Rust). So yes, building nice abstractions in Go will sometimes have a runtime cost. If that is unacceptable in a given application, then Go is not the right tool for the job. I'm not a Go zealot. If you need to do this kind of thing all over the place in your code base, then sure, don't use Go.
(2) If you are dealing with large arrays of non-zero ints (such that the overhead of boxing would be significant) then you could always define NonZeroIntArray.
I wouldn't use it for anything too complex, or requiring a GUI. Mainly because I'd be much better off using the languages in my day job that I'm more familiar with.
So if you want to do everything in your power to prevent a tool you think is terrible from being more popular - you can do very little apart from writing opinion in blogs or forums, improving the tool, or provide better alternatives.
Shitting on a technology might seem like the least noble of the 3, but it’s likely quite time-efficient compared to the other two. It’s certainly not useless.
I can agree with you if we talk about "tools" that attacks our privacy, our rights and our freedom: spend you time convincing people to abandon facebook or something similar and you'll do something good.
But in this case, it doesn't change much, imho.
I didn’t used to worry about this until it started happening to the sharp, experienced Scala/Java team I’m on by preference. Other teams are now deploying half-finished platforms on which only Go is supported fully, or sometimes at all.
You can write bad code in any language. You can write difficult to read code in any language. Every language has pitfalls and downsides.
Personally, I work in golang full time, and I adore it. Development is painless, and our codebase feels very easy to read.
Some languages make it much easier to write bad code. Some languages introduce safety checks that eliminate entire classes of bugs. So your claim that all languages are the same is very far away from the truth; not all tools are the same.
Every language has its own gotchas, and if you are not satisfied with it, coding some Java might help you gain a little perspective.
Also, you should read the article, it mentions reactions like yours.
Quite honestly, in what concerns choices of languages, my experience is that it's not so much the language per se that counts, but rather the tooling, developer community, and the availability of 3rd-party libraries.
For me, submissions that spread misinformation should be flagged. But just because you don't agree or like the article, doesn't mean that you should flag it. I guess this submission is flagged because some people don't like it but HN doesn't have downvote button, so they use the flag button as an alternative.
He should just own up to trying to shove the square peg into the round hole and move on with his life, rather than blame the peg. There is no shame in it. At one point or another, we've all done it.
The Go FAQ can help you understand where this is coming from: https://go.dev/doc/faq#go_or_golang
From https://go.dev/doc/faq#go_or_golang:
Is the language called Go or Golang? The language is called Go. The "golang" moniker arose because the web site was originally golang.org. (There was no .dev domain then.) Many use the golang name, though, and it is handy as a label. For instance, the Twitter tag for the language is "#golang". The language's name is just plain Go, regardless.
Rust is a chemical reaction, not a programming language. HTH. GLHF.
You've been here a decade! This is part of the HN experience. I'm sure we'll have an Atlas Obscura submission about the scintillating history of Serbian skin-contact white wines later today.
"Fine. It may well be that Go is not adequate for production services unless your shop is literally made up of Go experts (Tailscale) or you have infinite money to spend on engineering costs (Google)."
This is really wrong on so many level especially coming from someone that has shipped what exactly in production? What is the expertise of OP to tell people to not use a language that was proven to be very successful.
One of them a couple of years ago, detailing his experiences with using Go to accomplish something, contrasting it with how it would work in Rust.
And then this new one, which seems like a semi-response to that previous article hitting the HN front page again.
I wouldn't say he "keeps making" them, he's made two, and they're not coming out of nowhere. As he said in a comment of the most recent submission of his previous article (https://news.ycombinator.com/item?id=31193260):
> Author here: I wrote this in 2020, have changed jobs twice since. Both jobs involved Go in some capacity, where it's supposed to shine (web services). It has not been a pleasant experience either - I've lost count of the amount of incidents directly caused by poor error handling, or Go default values.
> Because that's the kind of code I inevitably end up being on-call for, and I'm tired of being woken up because of the same classes of preventable errors, all the time. It doesn't matter that I don't personally write Go anymore: it's unescapable. If it's not internal Go code, it's in a SAAS we pay for: and no matter who writes it, it fails in all the same predictable ways.
It's not that it's not his favourite language, it's that he's experiencing actual day to day issues at work caused (in his opinion) by the design of the language. I think that's fair to write about.
It seems to be part of the cycle of a language that gets widely adopted. I think the next step is that people stop fighting over it and just accept that it has some nice features and some bad parts.
Articles like this helps me (as a dev who's not using go very often) getting a feeling for what types of problem go might be good for and not. If it's well-written like this one, I think it's a good balance to the many positive articles that I've read.
Clearly the author cares a lot about language design. I get that he might be coming from a different background with different tastes. The fact that he doesn't like go isn't saying your time spent learning the language has been for nothing. Clearly a lot of code is being written in it, don't worry!
Here's some stuff he's done: https://fasterthanli.me/about I think he's entitled to an opinion either way though. Very interesting to see the response. Had no idea it was this touchy. I can get a bit weary seeing the millionth article saying that Python sucks because it doesn't have a feature of another language - but it also makes me want to try out that other language for myself.
that's why he didn't move on; we resurrected the topic.
He made a post two years ago in 2020 that was attacked by zealots yesterday when it hit the front page of hn and a post today that was a reply to that. It would be like if you were to post an opinion about perl two years ago, respond to people who attack you because they love perl more than their own children, and then be dismissively accused of crusading against perl when in reality all you did was post a response to people attacking you based on your two year old post. It's insane to say that two posts written two years apart is a crusade; I imagine most people who flagged the article and downvote its defenders simply can't accept legitimate criticisms of something they love too much to admit it has any flaws. So, I'll ask you your own question: "Why don't you move on, exactly?"
Most programmers don’t need someone to tell them which tools to use. Of those green enough to need it, they certainly shouldn’t look to the internet for advice and they absolutely shouldn’t look to HN or random blogs for advice. Instead they should find a local community which can offer them support when they stumble and point them to interesting job opportunities. Because those people are real and they can build relationships and connections which will be much more valuable than some measly programming language.
Many online authors - even if technically skilled - have quite an over-inflated sense of importance. What makes somebody think that their experience in domain X and country Y apply in any way shape or form to the hundreds and thousands of industries, countries and cultures all over the planet?