What's up with the anti-Golang shitposts lately? I liked fasterthanlime better when he didn't just sound like a card-carrying Rust zealot. This isn't even about the practicalities of his argument really; optics matter, and it's especially bad since the Rust community used to be known for its politeness and always being fair to other language communities.
The last time his "I want off" article was reposted[0], the comments were pretty rough, ranging from ad-hom attacks to (at best) misunderstandings of the arguments he was making.
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.
Both languages were designed by probably better programmers than most in this thread. For that matter, the vast majority of successful programming languages surely were. I'm utterly lost at how that relates to anything.
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.
> the Rust community used to be known for its politeness and always being fair to other language communities
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
Also honestly it's very toxic to anybody right of center. It's the type that says "we're so accepting" when really they only are of certain stuff. Lots of pronoun people and making the mascot "non binary" is needlessly shoving their ideology down people's throat. Not a problem in the U.S. because it fits with the corporate woke ideology but sure as hell gonna be if they want more worldwide community.
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.
As someone who both loves the Rust language and is very right of center, I've never found politics to pose much of a problem for the Rust community in practice. Yes there is occasional virtue signalling here and there but people don't let it get in the way of making programs that work.
Yeah, as someone who's gay and left-wing but equally irritated by the 'virtue signalling' stuff (my ideology is basically r/stupidpol), I don't find the Rust community too bad.
(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.)
Sorry, that's true, I didn't mean that literally no trans people (or gay people, black people, etc) use non-standard pronouns. I meant that it has nothing to do with their being gay or trans. Certainly some people may happen to be trans and also happen to use those pronouns.
There's still a lot of people out there who consider 'they/them' to be nonstandard when used for a specific person (as opposed to 'they/them' as a replacement for the generic 'he'; almost everybody opposed to that has come around or died).
Yeah, I should have clarified that I don’t consider that non-standard. Well, it’s semi-standard, I suppose. But yeah, I had in mind the ones more like ‘xe’ and ‘xer’.
> Their comment is about the (almost universally straight) people who demand to be called 'xe' or 'qwerty' or whatever.
The vast majority of people I’ve seen using neopronouns, or “it” or “they” pronouns (either exclusively or alternatively with classical gendered pronouns, e.g., “she/they”) have nonbinary gender identity or agender identity, and the vast majority of the exceptions are trans, and all the rest (i.e., the cisgender ones) I’ve encountered are one of bisexual, pansexual, asexual, or same-gender attracted, usually also with gender non-conforming presentation.
I have yet to encounter a cisgender heterosexual with nontraditional pronouns for their gender, though I am sure there are some somewhere doing it.
> The vast majority of people I’ve seen using neopronouns, or “it” or “they” pronouns (either exclusively or alternatively with classical gendered pronouns, e.g., “she/they”) have nonbinary gender identity or agender identity, and the vast majority of the exceptions are trans, and all the rest (i.e., the cisgender ones) I’ve encountered are one of bisexual, pansexual, asexual, or same-gender attracted, usually also with gender non-conforming presentation.
Yes, I'm talking about sexuality, not gender. Most of the people I know, and/or have met, who identify as 'xe' and suchlike, are straight and usually cis (in the sense of 'not actually trans' - they do often have 'gender identities' like 'genderqueer' which amount to 'I wear heels sometimes').
It's predominantly - in my experience - straight people who covet the counter-cultural aspect of being 'queer', but who face the small problem of not actually being gay or trans or anything else, which is easily remedied by adopting one of those slightly-meaningless 'gender identities' which uniformly amount to 'I'm a teeny bit [masc/femme] sometimes'.
(I literally had a conversation with someone once who argued that watching porn didn't mean they weren't asexual. They also happened to be in a long marriage with a husband they no longer slept with, though they once had. It took all my strength not to say "lady, you're not asexual or queer, you're just a bored housewife in a dead marriage".)
Yeah, same. They’re both nice languages, they both have great tooling, and they both have areas where they shine. I don’t see them as competitors really, but “You can also implement X in Y” is a fairly useless statement, of course you can.
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 …)
I might not agree with fasterthanlime's opinion on Go (9/10 times I would opt to use Go over Rust), but calling this post a "shitpost" is unnecessarily dismissive. The post, like his previous post on Go, contain well thought out points that add to the discussion at the very least. Also I like his sense of humour :)
Edit: though, I suppose saying Go is not designed is also equally unnecessarily dismissive and hyperbolic.
Well-thought out, how? It's flogging a dead horse in a transparent, deliberate attempt to create controversy and flamebait. That's a shitpost pretty much by definition.
I moved from Austin TEXAS to Portland to get away from the heat and forced conservitism. I also moved to Portland for the weather (cool and wet vs hot as hell and dry). I don't have to use my air conditioner!
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.
Calling a well documented blog post, even one you disagree with, a "shitpost" should be an embarrassment to you.
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?
But he doesn't dive deeper! If anything, he's more shallow. This just doesn't read like an honest appraisal of Go's pros and cons to me, he already has his mind set from the beginning (ok, we knew that two years ago already) and is just heaping on invectives: "one really good bit does not a platform make", "Evidently, the Go team didn't want to design a language", "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)" etc. etc.
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.
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.
I would consider "Evidently, the Go team didn't want to design a language" to be at least borderline invective. From dictionary.com, definition 3: "an insulting or abusive word or expression". Yeah, that's pretty clearly insulting.
"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.
"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.
I'd choose Node or PHP over Go for a greenfield project. (Although I can't think of a case where I'd choose them over Python.) All of those languages at least give me an FFI escape hatch in case I need it that isn't absolutely horrendous, both from a dev experience standpoint and a performance standpoint.
Yeah, preach. I wrote Go professionally for a couple of years, and I write Python now (I know, I know, but they pay me a ridiculous amount to do it). I would choose almost anything, including JS, over Go. It's a good language if you need to write highly concurrent high-level code with low-skill devs, but not for much else, and even then it requires an inordinate amount of menial boilerplate work to get simple stuff done. Having said that, I would still choose it – indeed anything - over Python.
Reminder that I'm not part of /any/ Rust teams, not part of the Rust foundation, etc. etc. My site is my space, and it is specifically designed to let me post my point of view without having to bear ( ) the responsibility of speaking for entire communities at once.
It's important for individuals to be able to do that, and it's on everyone else to make the distinction.
Thanks for your article. I hope you can ignore all the vitriol. It's embarrassing to see what I thought was a generally intelligent community having a collective tantrum because someone was mean about its favourite toy.
The point of these articles and other opinion-making (such as right here on HN) is that we don’t choose tools. We are given the popular tools. They are popular because other developers chose them at the companies were we work. And they chose them because they thought they would be suitable for the purpose.
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.
Why should I care if Go/Rust/anything else becomes popular? I don't expect that if I don't "fight" against a language I'll be forced to use it in 2 years: there will be space for different techs, tools and so on.
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.
> I don't expect that if I don't "fight" against a language I'll be forced to use it
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.
> Go's confusion between "type aliases" and "newtypes". The only way to make a newtype in go is to make a separate package with an opaque type and use interfaces for indirection, which is costly and awkward.
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.
Still not seeing it. You don't have to use interfaces to make an opaque type. A package boundary is sufficient.
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.
You can define NonZeroU64 in Go exactly the same way it's defined in Rust – as a struct with a private field. No interfaces required.
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.
> But I was wondering why you would want an opaque type internally to a package.
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.
Zero initialization is a pretty fundamental concept in Go. I can see why people might not like it, but if you are trying to prevent a large number of types from being zero initialized then you are just going against the grain of the language. In most cases you can arrange to make the zero value valid.
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.
> Zero initialization is a pretty fundamental concept in Go.
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.
>There's nothing particularly special about it other than it being an explicit choice
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.
I think that was the author's point. You _can_ use them this way but it's a hassle and most people don't do it. The language itself doesn't protect you against zero-initialisation errors, even though it could.
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:
Using interfaces here seems unnecessarily complex. If the default zero value were an issue in practice, I would just define
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.
Oh no, here we go again. So you don't like Go (no idea what this "golang" thing is everybody keeps talking about)? That's cool! You don't have to write yet another blog post saying that you really, really don't like it, with more strawman arguments and again quoting that quoted-to-death quote by Rob Pike about Go being designed for stupid developers, we got you the first time!
I'd just to point out that I think fanboys are not that useful and you have the right to don't read the article as much as he has the right to write it..
I dunno, aren't you just as guilty of not moving on? If Go is garbage, you defending it isn't going to suddenly correct that. Also, it's called golang because that's what a lot of people call it (i.e. https://www.reddit.com/r/golang/)
Then you could also say "He's called a [insert ethnic epithet here] because that's what a lot of people call him" - but I hope we can all agree that would be a bad idea?
I don't have a horse in this race, but as for "Go" vs. "golang" - I imagine people use "golang" as it's a bit more clear that you're talking about a specific thing (the programming language), as opposed to a fairly common English word (and a board game, and probably some other uses).
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.
Blog post is just one part, This person is whining about critical feedback received on HN at Twitter. Screenshotting users and comments for Twitter likes. For someone who doesn't like Go , they surely obsesses a lot about it.
Easier to search for things online with a unique moniker. I would use "golang" whenever I would talk about the language to IT people who may not know about it.
Yeah, "golang" is the standin for when "go" is already taken (https://github.com/go). But still, the official name is "Go", and there's no good reason to not use it in the title of a blog post (other than SEO I guess)...
My site has its own search engine (just sqlite full-text search, nothing fancy) and searching for "go" is entirely unhelpful. So yes, external and internal SEO.
The author obviously doesn't like Go. Ok, he can have his own opinions.
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.
For me, the goroutines + channels + select is what makes Go leagues above any other popular language.
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.
As I understand it, he's talking about select/alt (i.e. guarded commands from CSP) being a native feature of the language rather than callback-based approaches. It's possible to implement CSP channels in most languages, but they're not first-class like go.
> It's possible to implement CSP channels in most languages, but they're not first-class like go.
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.)
Defaults matter because friction matters, but friction there is largely removed by having a decent package manager, so I personally don't think it matters (though the topic of "what batteries should be included" has far from run its course).
The minute you put language constructs in the base syntax and runtime of the language, it's a lot more than friction. Love it or hate it, Go is inextricably tied to CSP.
It matters a lot IMO. Most systems programming languages can implement whatever concurrency primitives you care to use, but most people default to whatever is provided by the OS/language because concurrency is hard. Having first-class syntactic sugar over a concurrent runtime bakes in a certain programming style in the language, which strongly determines how the language is used and the programming style. Go comes out of the Newsqueak/Aleph/Limbo family which has a very different style of programming compared to a lot of other languages.
I think there's a difference between 'in the standard library' and 'in the language', though. They seem to be praising Go for doing the latter. And the technical language in which it was expressed gave me to think there was some technical motivation for that, as opposed to sociological - but if what they meant is what you said, then I certainly wouldn't disagree with that.
I'd argue that Go is explicitly sociological. Most of the novel ideas in Go (process calculi/guarded commands in a concurrent language) were originally embedded in Cardelli and Pike's Squeak back in the 80s. The rest is trying to standardize the way scalable web applications are built.
It's the opposite - pointing out that X can just as well be implemented in language Y is the irrelevant point because anything can be implemented in any turing complete language.
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.
Yeah, I guess I overestimated the complexity of such mechanisms. I mean, still, you need some kind of runtime for the asynchronicity, but the Rust version seems fairly OK (apart from the function coloring, but that's another story).
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.
I interpreted "at the same time" as "without blocking",
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.
Gotcha, sounds like we interpreted it differently. I appreciate you taking the time to clarify, I am an old hand as JS but didn't know true parallelism had become feasible these days. Thank you, sincerely!
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.
Is the read() call running a task in the background? In that case, that's equivalent to launching two threads. For n queues, you'd need n tasks, and they'd hang in the background in case they never get a result and take up memory.
here's some documentation on how to do it in C++ with boost in 2008, before go even existed: in this example two threads pull events from a single queue.
I'm not really experienced with Boost so I'm having a hard time understanding what that snippet does. Is there any example that is completely self-contained (or at least limited to STL)?
Boost's io_service (since then renamed as io_context) is an implementation of a thread-safe event queue. Threads can put events in the queue (timer events, asynchronous read and write requests for networking, but one can also just push a generic lambda).
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
A couple of things to add to this: the language spec is like a page long and very readable. Also the entire compiler toolchain and everything is written in Go and doesn’t depend llvm etc. This doesn’t help the developer but is a pretty unique aspect of Go.
> Also the entire compiler toolchain and everything is written in Go and doesn’t depend llvm etc.
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.
Can you clarify further? Otherwise this seems like a shallow dismissal, which is against our HN community guidelines, and makes for uninteresting discussion.
Programming languages have a large amount of irreducible complexity. A page long spec for a language more complex than Brainfuck cannot represent the critical distinctions correctly.
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.
> Without that clarity, you end up with incompatible, spec-compliant implementations.
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?
The only other person in this conversation is you, so not exactly the most representative of samples, but it is an interesting perspective. I've not heard anyone else consider the same HTML resource to be more than one page before. In fact, the term "single page application" hinges on a single HTML resource in the same way. The application may have more than one screen of content, but it is still considered one page.
oxnrtr is in this conversation too. And when describing the length of a document, "pages" universally means the number of printed pages it would occupy, not the number of computer files it's stored in.
There is no sign of anyone else participating in this conversation. Else we'd have heard their input by now. A page referring to a single HTML resource is standard nomenclature. Paper is antiquated technology, not how the Go specification is distributed, and carries no relevance to this discussion.
This has perhaps been my biggest pain-point with Golang..
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.
Lol, I thought it was some sort of Joke when I first learnt it. And kept hunting for the 'proper' way. When I realised this was serious, I banged my head against the wall.
As an operations person who cares about deploying repeatable systems at scale, Rust's package management (and the toolchain's "packaging" itself) is an absolute nightmare. But at least I with MUSL can I have a 100% statically linked binary, which really should have been easier but that's another argument.
Seriously though, Rust team needs to have its Come To Jesus moment about not piping shit off the internet to sh.
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).
You can use cargo vendor to import specific versions of your dependencies into your project for easy auditing, or cargo fetch to simply be more explicit about when to download from remote repositories (when combined with the --offline flag). The typical Rust "dependency" is small; it's more like a single .c or .cxx file than a conventional "library", except that it also plays nice with generic code unlike these languages.
I agree that Rust does suffer a similar problem to NPM where there are many small packages that people will use together.
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.
Go tooling is awesome compared to other language ( Rust included ). Everything is built it and you don't need something external, everything works out of the box dependencies included, it has been the case for 4 years with go mod.
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:
Yeah, it's pretty obvious to me the author doesn't like Go and some of the arguments raised really aren't concerns in (my very limited part of) the real world.
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.
From the article: "[...] as developers get more and more senior, they tend to ignore more and more problems, because they've gotten so used to it. That's the way it's always been done, and they've learned to live with them, so they've stopped questioning it any more."
Hmm won't this apply to any language and therefore not a valid argument? A: language FOO is a great language! B: You are telling lies because you are fluent in this language and ignore the problems in the language.
It seems to me there are two ways to evaluate the "beauty" of a language: there's the people who love the language theoretically (well constructed, coherent, nice typing, etc) and then there's people who love the usefulness/utility of the language.
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.
Yes. This is true of any environment. That's why I ask new hires to write down every WTF moment, every question and every idea for improvement they have as they get up to speed on our systems. Just because I'm used to avoiding a problem doesn't mean the problem must exist.
IME there are sort of two kinds of people here. People who are "fans" and defend "their" toy and people who don't. For example, I've been using Python for a very long time. I'm a "Senior Python Developer" so to speak. I quite like it for various purposes, but I can also talk your ear off about problems with Python.
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.
As developers get more and more senior, they have a different perspective on what constitutes a "serious" problem. Also, as developers get more experience with a language, they get better at doing things in ways that are idiomatic for that language - they work with the language instead of against it.
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.)
He made some good points in the article but this particular paragraph stuck out as being hypocritical. The entire motivation of him writing this was because he felt people dismissed his previous article out of hand. And then he makes a remark that preemptively dismisses any rebuttal out of hand (and as others have pointed out, can equally apply to any language so hardly a point worth complaining against Go specifically).
As developers get more and more senior they tend to ignore more and more problems because they realize the majority of them actually don't matter very much to doing useful work.
> as developers get more and more senior, they tend to ignore more and more problems, because they've gotten so used to it.
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.
Im a go noobie so I had a question about the following.
> 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.
They mention it elsewhere in the post, but there are a few types that are essentially pointers but don't look like pointers. Slices, maps, interfaces, and channels (and also functions, but that isn't that important here). So you cannot just think that anything that doesn't have an asterisk is a copy.
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 see! Thank you for the example that was helpful.
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?
I actually agree with the author that nils are a massive pain in the arse most of the time, and that Go would be much better off without them.
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.
The problem is that you can still mutate a copy. But does the person mutating the copy realize it's a copy, and that it won't propagate?
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?
Having followed language wars for a while as a relative newcomer to programming (longtime lab rat type), my present conclusion is that languages are just tools, comparable in my experience to different laboratory techniques and instrumentation. Different tools are appropriate for different purposes. Some are a lot less convenient than others to set up and use. Some are so obscure that nobody really supports them anymore, and if you want to use them you'll have little help with troubleshooting. Some are popular fads that come and go, as people realize there are better options available for particular jobs.
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 definitely see that languages are just tools and you pick the right one for the job.
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
> 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.
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).
i feel like that C example isnt fair cause you cant have functions bound to types right??
youre not calling change(a) but instead calling a.change().
i havent learned any rust yet, im waiting till im a little better at go so i can be comfortable at work before doing so:)
whats the advantage of creating a function of type struct a rather than creating the C equivalent of change(obj structAtype) ? to me now, it seems that it should be expected that an a.Change func will mutate A
i see what you’re saying that with Go it cant always be easy to check if its mutating what you’re calling or not. its giving the developer more options to use stuff by ref/val which gives more power but more room for mistakes.
> it seems that it should be expected that an a.Change func will mutate A
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 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?
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.
Elaborating on 3/4: the domain experts don't need to be compiler experts too.
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.
This is probably the best talk ever on the general topic: https://m.youtube.com/watch?v=_ahvzDzKdB0 [Growing a language by Guy Steele], seriously give it a look if you have the time and do put up with the seemingly strange start.
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.
The author definitely understands that. The point is: when you don't have operator overloading you get stuff like that which does demonstrably confuse many people
In languages with operator overloading this just isn't a concern at all because == does the obvious thing
== should do the obvious thing. But with operator overloading, it's not clear that it does, unless you need to read the implementation of == very carefully.
The obvious thing is to check if the contents are equal. I don't know what cursed C++ you're looking at if you're finding a ton of string implementations not doing exactly that...
Usually you do want to compare by value, so it should get the obvious syntax. When you want to compare by reference, you should write if(&a == &b) or ReferenceEquals(a, b) or something.
I don't get the problem with operator overloading. Yes, it can be implemented badly, but so can the equals() override. You can't even call a.equals(b) safely in many programming languages because either a or b could be null. I've run into so many null pointer exceptions in Java because someone wrote a.equals(b) when they clearly meant a!=null && a.equals(b).
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.
Without operator overloading, it is basically impossible to adapt a language for numerical computing/data science applications. Yeah, operator overloading is a powerful tool that's easy to abuse; no, it's not optional if you want the language to succeed at all in a whole bunch of applications.
> Also: operator overloading is the spawn of Satan.
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.
> Why does the Go compiler suddenly care if we provide explicit values now? If the language was self-consistent, it would let me omit both parameters, and just default to zero.
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.
It is a very minor point and could easily be removed from the article, but I had that realization while writing those samples and it stuck with me.
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.
Meh, this blog post is a rather subjective rant. I can offer the opposite opinion, although I'm not sure that's helpful. The more I use Go, the more I like it. I'm very productive with it, similar to -- though not quite on a par yet -- the productivity I had when I was programming in REALBasic. Most of the benefits of Go result from the tooling and the availability of 3rd party, open source libraries with permissive licenses that are "good enough" for my purposes. I could probably also use Python instead and benefit from even more 3rd party support, but there would also be downsides to it. I appreciate Go's compact executables and easy deployment.
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.
I believe he wrote a binary diff/patch system in it. If I had to do such a thing in Go, I would also hate Go.
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.
I generally like go and also see its problems as the author does.
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 Go you can develop a web service with few to no external deps or frameworks, compile it into a single, static binary and then deploy it in a minimal distroless container.
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.
Sorry if it was unclear, I was specifically commenting about C# with regards to build, test, deploy cycle because I don't have much experience with Java. It's good to hear that it's not slow or cumbersome.
I dont know them as well as I do Go but the libraries (java especially) don't feel as coherent as the go libraries do, though that may be subjective and to do with my lack of experience with them. In addition both of these languages are large enough and have projects structured in such a way as to make it difficult to just use a text editor, which i consider a barrier to me just 'cracking out' a project.
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.
This is an opportunity piece written for no reason other than to ride the wave from a recent re-post of a 2 year old blog article. It offers nothing over the original blog post and it's difficult not to be cynical about the authors intent with this article.
>ride the wave from a recent re-post of a 2 year old blog article
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.
I didn't care in the last post and I don't care about this one either. I care about my productivity, I care about my team's productivity, and I care about getting stuff shipped. Those are the things I care about, and go works great for that. If you care about those things too, and you're using go, you shouldn't stop using go.
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.
I think your POV works in your very specific world view. As a head of an agency, hiring a developer that knows mainline languages serves a lot of good in hiring, keeping teams happy, and low turnover. We've all seen the agencies that hop on a new, hip language - only to go up in flames 6 months later because they couldn't find talent for their narrow viewpoint.
This isn't an argument against Go, moreso that your perspective is objectively elitist.
I don't think that the author is telling anyone to stop using Go. They are suggesting that there should be a better alternative, and there are lessons to be learnt.
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
I mean, this is Hacker News. Not corporate programmer news. Building a successful company has very little to do with tech choices. But that doesn't mean we shouldn't discuss these things as tinkerers and hackers.
> Building a successful company has very little to do with tech choices.
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.
So many companies succeed _despite_ their tech choices and so many fail also despite their tech choices.
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.
I use Go. I'm not a Rustacean. I've also used a whole bunch of things that are not Go.
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.
The author of the blog post literally covered it in the article he wrote two years prior that's linked in the first sentence.
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.
Hmm not really, the only thing is the default http client that has no time out which is the case in many languages. The only issue I had was about idle connection and DNS resolution.
And the ticket you show does not show any issues on the Go side.
> GRPC and protobufs are f'in messy and full of footguns.
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?
protobuf has issues with optional fields, as was pointed out in the post. Couple that with a language with a weak type system like golang (no sum types), and it's very verbose and slow to develop in. Targeting better languages helps alleviate the latter.
I believe that you are perfectly productive with Go, the article hasn't claimed otherwise.
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?)
The article's author has written and shipped tons of quality Go code that is still used in production by many people, myself included.
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.
As a beginner learning Go for the past 3 weeks, thanks for the last paragraph.
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?
Rust is a general purpose programming language. It can do low level systems programming but it's also highly capable of doing web backends, web frontends (wasm), game design, small utility scripts, etc.
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.
Rust is indeed a general purpose programming language, but unlike the sibling poster I'm not going to sell it for low-level systems programming _and_ other things just because it can and has some (hit-or-miss depending on the domain) crates for them.
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.
Thank you for the detailed explanation! That actually clarifies a lot of things.
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? :)
> 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.
> I didn't care in the last post and I don't care about this one either. I care about my productivity, I care about my team's productivity, and I care about getting stuff shipped. Those are the things I care about, and go works great for that.
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 use Go. I love Go. The reason why is pretty simple for me: It gets things done in a way that feels comfortable and has strong tooling. I am well aware it has shortcomings. I occasionally even run into them. However, as someone who doesn't particularly /like/ writing code, and instead sees it as the inevitable requirement of creating things that do something for me, I really appreciate that Go for the most part doesn't get in my way so I can make things that do something and move on.
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 just don't see the issue with Go. After dealing with inscrutable errors in some python code that interfaces with OpenSSL. Or dealing with impossible to trace errors in Spring Boot. Or Javascript? An absolute nightmare from start to finish. There's something extremely nice to get an error, place a breakpoint and trace exactly (even if it's a third party library) that error is happening.
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.
> Or dealing with impossible to trace errors in Spring Boot.
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.
Spring boot is not, in any way shape or form, a "simple" java "library". Not only is it a complexframework, it also uses features very few java programmers are exposed to, such as runtime code generation.
You can use runtime code generation with mere java.lang.reflect.Proxy. Yes, Spring is slightly more powerful than that, but it's not something magical. Runtime code generation is a simple concept.
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).
Not a CS, but a damn good technical accountant and well self-taught developer.
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.
I seriously doubt that Go would compile faster than Java. Like, neither does any reasonable amount of optimization, and I have never heard Java considered slow in compile time. Sure, one can do some very cryptic module-graph with their chosen build tool, but otherwise it should be as fast as it gets.
One more thing to note (on this post that is already on page 2 of HN after 47 minutes):
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.
I really don't know enough about Python to discuss it, so I usually don't. There have been some semi-recent developments re typechecking Python (see https://dropbox.tech/application/our-journey-to-type-checkin...) that have made me interested in trying out for real.
I use Go for simple CLI apps and it's pretty dang good for that. They're used by coworkers, contractors, and clients so the fact I can just send over an executable for any OS is really nice.
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.
It's the matter of one's opinion, I personally love it. The design has +n years of experience behind it and stands on shoulder of some serious people (yea you already know who they are).
Every language has its own gotchas, and if you are not satisfied with it, coding some Java might help you gain a little perspective.
I'm not emotionally attached to Go, I want to learn rust, and I'm old enough to realize the search for the perfect tool is utter folly. So the inevitable religious wars that surround critiques like this don't move the needle for me.
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.
560 comments
[ 3.3 ms ] story [ 347 ms ] threadI 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.
[0]: https://news.ycombinator.com/item?id=31191700
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?
The vast majority of people I’ve seen using neopronouns, or “it” or “they” pronouns (either exclusively or alternatively with classical gendered pronouns, e.g., “she/they”) have nonbinary gender identity or agender identity, and the vast majority of the exceptions are trans, and all the rest (i.e., the cisgender ones) I’ve encountered are one of bisexual, pansexual, asexual, or same-gender attracted, usually also with gender non-conforming presentation.
I have yet to encounter a cisgender heterosexual with nontraditional pronouns for their gender, though I am sure there are some somewhere doing it.
Yes, I'm talking about sexuality, not gender. Most of the people I know, and/or have met, who identify as 'xe' and suchlike, are straight and usually cis (in the sense of 'not actually trans' - they do often have 'gender identities' like 'genderqueer' which amount to 'I wear heels sometimes').
It's predominantly - in my experience - straight people who covet the counter-cultural aspect of being 'queer', but who face the small problem of not actually being gay or trans or anything else, which is easily remedied by adopting one of those slightly-meaningless 'gender identities' which uniformly amount to 'I'm a teeny bit [masc/femme] sometimes'.
(I literally had a conversation with someone once who argued that watching porn didn't mean they weren't asexual. They also happened to be in a long marriage with a husband they no longer slept with, though they once had. It took all my strength not to say "lady, you're not asexual or queer, you're just a bored housewife in a dead marriage".)
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.
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.
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:
The reason you can do things like: is that because numeric literals in Go are not ints, they are untyped [0].[0] https://go.dev/blog/constants
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.
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:
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:
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: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.
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.
https://github.com/golang
Rust is a chemical reaction, not a programming language. HTH. GLHF.
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
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'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.
https://go.dev/play/p/Iqd84_eAMNF
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
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.
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:
> 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
However,
> It reads as an asshole college sophomore who rips on plebs who dare to have different perspectives than them
This is going a bit too far, don't you think?
> 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.
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.
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.
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.
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.
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.
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.
https://pkg.go.dev/net
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 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 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.
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.
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.
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.
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.