
Go hits the concurrency nail on the head - nikbackm
https://eli.thegreenplace.net/2018/go-hits-the-concurrency-nail-right-on-the-head/
======
accnumnplus1
For me, "great tools like the race detector" sums up the article - the
detector is fallible, and although every point in the article has some
validity, they also could all be argued against, and the result is a bit of a
house of cards. So for me, Go at work by order, Erlang at home by choice.

------
iainmerrick
_Mixing threads with event loops is possible, but so complicated that few
programmers can afford the mental burden for their applications._

This is just Apple's Grand Central Dispatch model, or the event loops used
internally in Chromium. It's not complicated at all, it's a very practical and
productive approach.

------
iainmerrick
Do people really find CSP a good approach to concurrency? I've always thought
that channels were a relatively poor choice of fundamental primitive --
channel-based concurrency is tricky to get right and not very composable.

Most viable concurrency primitives are in some sense equivalent (you can build
condition variables out of channels and vice-versa, say) but that doesn't mean
they're equally good.

Java has per-object "synchronized" and "notify" as its core constructs, but
it's a bad idea to use those directly for day-to-day concurrency tasks. Much
better to use library classes like ThreadPoolExecutor and Future.

In Go, do you tend to use channels directly, or do you use higher-level
wrappers?

~~~
apta
Exactly. I get the feeling that people who claim golang gets concurrency right
have not used the much more powerful constructs in the Java standard library.
They keep getting better with things like CompletableFuture which was added in
Java 8, and coroutines which are going to be added to the JVM sometime down
the line by means of project Loom.

Composability in golang concurrency constructs is non-existent. Having to
manually manage channels to indicate errors, return values, and completion is
error prone and does not compose, and is subject to race conditions. I don't
see anything that I can do in golang that I wouldn't be able to do using
Java's futures, while still being more composable and easier to reason about
in Java.

------
thinkpad20
I think the author might be overstating how unique Go’s position is in terms
of making concurrency easier. Haskell has the best concurrency story of any
language I’ve used. Super lightweight green threads, simple concurrency
primitives like MVar and STM, good performance (possibly requiring tweaks, but
not bad out of the box). Referential transparency (by which I basically mean
immutable data) makes sharing data across threads much easier and in some
cases more performant by allowing you to avoid copying. Plus, you have a type
system which makes it much easier to reason about your code.

All that being said, I haven’t written Go and can’t compare the two. Also,
Haskell doesn’t support the actor/message passing model out of the box
(although libraries exist for it) or prevent deadlocks (although the
immutability helps a great deal here). BEAM languages, clojure, rust, pony and
others all have their strengths here — again, this doesn’t discredit the
article at all but the idea that Go is the clear winner is debatable.

~~~
jerf
In terms of raw capability, Haskell is the concurrency winner. Not only does
it have basically every paradigm, they even all work together reasonably well.
(Not perfectly, but reasonably well.) But I don't think you can argue Haskell
is mainstream. It continues to be on a fairly slow growth trajectory from what
I see, but it's not in any danger of cracking into the top tier language set
anytime soon. Go just might in another 5 years.

~~~
thinkpad20
True, but I think that the "mainstream-ness" of Go (debatable as it is) was of
secondary importance to the article, which didn't even mention that there are
other languages out there which solve the concurrency problem in other/better
ways. Whether or not a language is mainstream is more or less a matter of
opinion, in any case. Certainly Haskell, Rust or Erlang (to name a few) are
probably less widely used than Go, but none of them are obscure, so to not
mention them at all suggests that the author is either being disingenuous, is
not aware of those languages' capabilities, or simply forgot.

------
kahlonel
Apparently there is a C library for green (Go-like) threads called "libdill"
or "libmill".

------
erik_seaberg
> Proper use of channels removes the need for more explicit locking

If you're lucky. Sharing mutable state is unsafe by default (map writes can
crash!) yet very common and the language doesn't help you avoid it. A good
language for concurrency would also make it easy to switch between sync and
async method calls; the trouble with channels is they don't support passing
errors and panics without rewriting everything to wrap them in a pseudo-
generic struct.

~~~
camgunz
Go's answer to this is tooling, -race in this case. I think Go's general
strategy of moving complexity/functionality to external tools is a little
underexamined -- it's definitely interesting, especially as it's kind of a
middle path between "language with lots of helper stuff" and "IDE with lots of
helper stuff".

~~~
erik_seaberg
It's better than nothing, but it's so expensive they don't really expect
anyone to use it at prod scale. That's a recipe for letting catastrophic bugs
do damage for hours or days.

------
favorited
> Mixing threads with event loops is possible, but so complicated that few
> programmers can afford the mental burden for their applications.

Correct me if I'm wrong, but doesn't basically every UI framework from the
last 20 years do exactly this?

~~~
Matthias247
They do, but I think also that possibly every user of such a framework has at
least once created a concurrency issue once in their life. All of these the
issues below are things I have seen (and created) over and over in these
environments:

\- Trying to access UI framework elements from the wrong thread

\- not understanding that a callback inside one of the included libraries is
not running on the UI thread but from somewhere else

\- callbacks capturing references to objects which already had been destroyed
(but the callback had been queued and can't be cancelled), and accessing those
objects later on

\- deadlocks, due to mutex locking in both callbacks as well as the "normal"
code.

There are ways to minimize the amount of issues, e.g. always deferring
asynchronous callbacks to the next eventloop iteration, making sure that
callbacks are queued on the main thread instead of an arbitrary one, deferring
object deletion behind all other callbacks, etc. But these are on the harder
to learn, teach and enforce side.

Therefore I would agree with the author of that article that the mixture of
eventloop-based programming and multiple threads is the hardest combination.

------
tracker1
Regarding one of the last comments, I'd say the two biggest reasons for using
Node over Go, at least initially. Prototyping speed, and a stronger connection
to a web front-end. I've really not seen any other language/platform work
faster for developing a huge variety of implementation details than JS/Node.
IT's a really good balance of performance, flexibility and ease of
development.

Is it a Panacea? Of course not. That said, I think that starting more
monolithic an breaking pieces off as needed is a strong approach, best started
with Node. From there, you want different pipelines/processes/queues/workers
in other platforms, great. Write that piece in go+grpc and call it from your
api endpoint server.

So many times I see devs want to go the optimal X, without even considering if
"optimal" is needed, and if it's prudent to start with.

~~~
fixermark
And the main reason to not use Node: JavaScript.

------
bunderbunder
> _Programming with threads is hard - it 's hard to synchronize access to data
> structures without causing deadlocks; it's hard to reason about multiple
> threads accessing the same data, it's hard to choose the right locking
> granularity, etc._

That's a list of problems that are specific to mutable state that is shared
among threads.

As the old saying goes, "If it hurts, don't do it."

We've had ways of doing multithreaded code that are easier to reason about for
_decades_. They really do work quite well. Why people doggedly insist on
pretending they don't exist is a perennial mystery to me. Even if your
programming language wasn't kind enough to include a better concurrency model
in its standard library, there are always third-party libraries.

I realize my experience isn't universal, but, personally, I've discovered that
there's precisely one scenario where I ever need to resort to code that
involves mutexes: When the business requirements and the performance profiler
have conspired to kidnap my children and hold them for ransom.

~~~
Izkata
> We've had ways of doing multithreaded code that are easier to reason about
> for decades. They really do work quite well. Why people doggedly insist on
> pretending they don't exist is a perennial mystery to me.

Not a single one was mentioned during my CS undergrad; shared-memory threads
were, many times. I think simple ignorance is the answer.

~~~
bunderbunder
I think a simple love of complexity is also part of it. It's fun to do things
that make you feel clever.

One experience I've had more often than I'd care to admit is diving into a
multithreaded module with the intent of fixing a race condition bug, and
finding that I could simultaneously remove the bug and realize a healthy
performance improvement by making it single-threaded.

Which, for that matter, is another reason to be wary of mutexes and shared
mutable data: Memory barriers do really impolite things to pipelines and
caches in a modern CPU.

------
siscia
Leave aside the extremely interesting engineer behind th go scheduler and
goroutine.

The abstraction that goroutine provides is a simple, indipendent, isolated
unit of execution.

You start it, and that is all you can do with it.

No way to set priorities, decided when to stop it or inspect it.

After the goroutine start the only interface that you get is a channel where
to push and pop stuff from. Which is just too limited consider the complete
lack of genetics.

It is really the best we can come up with?

~~~
apta
I'm not sure how people can't see that futures (as in Java's
CompletableFuture) is much superior to golang's approach exactly due to the
reasons you mention.

You can also have the actor approach (e.g. Akka) to set priorities, have
monitoring hierarchies, and introspection.

------
da02
What other languages/platforms are out there that are as good with concurrency
as Go?

------
spinningslate
Don't disagree with the challenges about other languages being equally
applicable. My first thought was also "Erlang does it at least as well as Go".
Reasonable challenges on whether Erlang (or Elixir/Pony/...) are mainstream
though.

For me the more valuable point is not that Go specifically gets it right: it's
that async - as implemented in javascript/python/java/c# and so on - is
fundamentally wrong. These two quotes get to the heart of it:

>The core idea is that in the asynchronous model we have to mentally note the
blocking nature of every function, and this affects where we can call it from.

>The fundamental issue here is that both Python and C++ try to solve this
problem on a library level, when it really needs a language runtime solution.

I've said for a while that async as implemented in javascript et al is the
"GOTO" of concurrency - and should be considered equally as harmful as
Dijkstra's observation on GOTO, for many of the same reasons [0].

[0]
[https://en.wikipedia.org/wiki/Considered_harmful](https://en.wikipedia.org/wiki/Considered_harmful)

~~~
notriddle
Shared-memory threads are worse than async/await. I'd rather have GOTO than
data races, since, while it might be hard to model in your head, at least it's
deterministic and using it wrong doesn't cause UB.

p.s. [https://vorpus.org/blog/notes-on-structured-concurrency-
or-g...](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-
statement-considered-harmful/)

~~~
spinningslate
Erlang doesn't do shared memory. It's a message passing, shared-nothing model.
I believe GO is the same.

~~~
tomjakubowski
Go is not shared-nothing: if you write a slice to a channel between two
goroutines, for example, the memory that the slice references is now shared
between the goroutines

~~~
spinningslate
thanks for the correction.

------
rustcharm
I disagree. You really need "shared nothing" and messages. The only language
that really hits the concurrency nail on the head is Erlang, which we use
extensively in our business.

~~~
truth_seeker
Erlang is latency sensitive and Go is throughput sensitive

------
dmead
This picture is a bit rosey, no? Literally the only deadlock I've come across
in years has been in go.

~~~
gjm11
What fraction of the concurrent programming you've done over those years has
been in Go?

~~~
apta
A language claiming to be designed to solve the concurrency issue needs to
have a better story than what golang offers.

------
kasey_junk
Its fairly interesting that this article doesn't mention actors,
futures/promises and async/await style co-routines which are all extremely
available in all of the major languages available today and broadly used (with
the possible exception of golang).

Frankly, I think the concurrency story is one of the _weaknesses_ of golang.
Contrary to what this article says you cannot stop thinking about concurrency
in your code in golang like you can in some of the more avante garde
concurrency models (STM) and it doesn't provide nearly the sophistication in
type or post build checking of other languages.

~~~
wahern
All of those interfaces are trivial to implement in Go precisely because Go
implements a much stronger abstraction. By contrast, if you _only_ have those
other abstractions you're quite limited in how you can structure your code.
(You might not realize how limited you are, however, if those are your only
options.)

As the article says, most languages settle for those interfaces because they
can mostly be implemented as libraries with minimal refactoring of the
existing implementations and their execution model.

They have their uses but they're not universal enough. If you think actors,
futures, and async/await are so great, imagine having to write _every_
_single_ _function_ _invocation_ in that manner. It would be unimaginable
outside of some special-purpose declarative DSL. By contrast, the concept of a
thread--linear flow of _logical_ execution of statements and expressions--is
fundamental to programming. Even in functional languages with lazy evaluation
or automagic parallelism. It's a basic building block similar to functions.
And much like functions, it's incredibly useful to be able to easily compose
those blocks, which is where channels and message passing come into the
equation. One way to compose them is with actor and future interfaces, but
that's not the only way and not always the most appropriate way.

Threads ended up with a bad name because of (1) performance and (2) races, but
that conflates the abstract concept of a thread--a construct that encapsulates
the state of recursive function calls--with particular implementations.
Goroutines are threads, pure and simple, but [mostly] without all the baggage
of traditional implementations. (That they can be scheduled to execute in
parallel complicates the construct but also makes them useful for the same
reasons traditional OS threading constructs were predominately used, except
with much less of the baggage.)

~~~
kasey_junk
The biggest problem with go is that it’s _not_ easy to implement those as
libraries. This is a combination of the golang story around generics & their
opinionated strategy on concurrency.

Conversely most other modern, mainstream languages _can_ mimic golang
concurrency as a library.

~~~
wahern
You absolutely cannot mimic Go concurrency. You're not understanding (or not
appreciating) the function color problem mentioned in the article. See, e.g.,
[http://journal.stuffwithstuff.com/2015/02/01/what-color-
is-y...](http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-
function/)

~~~
kasey_junk
Or it’s possible I don’t agree with its premise that go solves the problem
better than other languages because it hides async behaviors from the type
system.

In practice it doesn’t. Asynchronous functions leak into golang
implementations in _worse_ ways in golang. Everything from the near universal
use of channels as poorly implemented promises to the horrendous Context being
passed to everything for cancellations. The golang concurrency story is _weak_
compared to any language that _has a story at all_.

------
dreamcompiler
Or you could just use the Actor model, which I like much better than CSP. (Or
at least I think I'll like it better when I finally understand it.)

~~~
sagichmal
The Actor model is trivially implementable with CSP.

~~~
notriddle
And CSP is trivially implementable using the Actor model. What's your point?

~~~
Matthias247
Depends. I would argue that e.g. Go's CSP approach is not implementable on top
of Akka, since actors there are not allowed to block on reception of single
message. They will always get messages pushed, so the blocking Go concurrency
constructs can not be built on top of it. It's obviously slightly different
with Erlang, where Actors can block.

I think the basic Actor definition does only say that Actors need to send
messages to others, but not whether they can block on reception of selected
responses. So it might be a bit undefined.

~~~
zzzcpan
Lack of blocking support in runtime cannot prevent you from implementing
waiting. Either through higher-order event driven code or just plain busy-
waiting style message exchanging until you receive your message.

~~~
Matthias247
Busy waiting won't work, since there will potentially never run another thread
that changes the thing you are waiting for (that's why e.g. in JS everything
needs to be async). There might be ways to model things a bit different (like
using Promises and other monadic abstractions), but things will never look the
same as in a blocking environment.

~~~
zzzcpan
It's a model, it doesn't matter how it looks.

------
erokar
According to the author Go make concurrent programming "the best experience,
by far, compared to other popular programming languages today."

I beg to differ. I fail to see why I should choose Go over Elixir/Erlang for
concurrency. Elixir's cuncurrency mechanisms are at least as good — and I
would argue better — than Go's, and Elixir as a language has an expressiveness
that Go lacks.

~~~
alberth
I really wish someone like Mike Pall (of LuaJIT fame) would work on BEAM to
make it computationally as fast as Go on raw performance.

~~~
notriddle
The BEAM is already much more performant than, for example, the Ruby reference
implementation. And look at what people have managed to build with Ruby!

~~~
jashmatthews
Erlang 19 is only 10% faster than CRuby 2.3 on a simple factorial bench. Web
throughput is even closer as lots of it is C extensions.

I haven't made the comparison myself for a while but I suspect it's still very
close.

------
pcwalton
Go concurrency _is_ just threads. It's a particularly idiosyncratic userland
implementation of them.

There are two claims here I'd like to unpack further:

1\. "I've measured goroutine switching time to be ~170 ns on my machine, 10x
faster than thread switching time." This is because of the lack of switchto
support in the Linux kernel, not because of any fundamental difference between
threads and goroutines. A Google engineer had a patch [1] that unfortunately
never landed to add this support in 2013. Windows already has this
functionality, via UMS. I would like to see Linux push further on this,
because kernel support seems like the right way to improve context switching
performance.

2\. "Goroutines also have small stacks that can grow at run-time (something
thread stacks cannot do)." This is a frequent myth. Thread stacks can do this
too, with appropriate runtime support: after all, if they couldn't, then Go
couldn't implement stack growth, since Go's runtime is built in userland on
top of kernel threads. Stack growth is a feature of the _garbage collection
infrastructure_ , not of the concurrency support. You could have stack growth
in a 1:1 thread system as well, as long as that system kept the information
needed to relocate pointers into the stack.

Goroutines _are_ threads. So the idea the "Go has eliminated the distinction
between synchronous and asynchronous code" is only vacuously true, because in
Go, everything is synchronous.

Finally, Go doesn't do anything to prevent data races, which are the biggest
problem facing concurrent code. It actually makes data races _easier_ than in
languages like C++, because it has no concept of const, even as a lint. Race
detectors have long existed in C++ as well, at least as far back as Helgrind.

[1]:
[https://blog.linuxplumbersconf.org/2013/ocw/system/presentat...](https://blog.linuxplumbersconf.org/2013/ocw/system/presentations/1653/original/LPC%20-%20User%20Threading.pdf)

~~~
pdeuchler
Nothing you're saying is wrong, but your perspective is totally off.

For someone experienced with C++, or say, Rust (ahem), obviously Go is a bit
backwards when it comes to concurrency and race conditions. Obviously you can
mimic go's goroutine stacks, obviously you can obtain fast thread switching.

But Go isn't targeting C++ or Rust, and it's not targeting the domains those
languages are best at (although admittedly there are some overlaps between Go
and Rust). Go is trying to replace Ruby, Python, and JS. For programmers who
only know those languages, or haven't had the opportunity to work in more
"heavy" languages, Go makes it dead simple and intuitive to do things that
previously would have been completely out of reach. All you're arguing is that
if you go farther "down" the stack so to speak you can accomplish everything
Go does, which of course is true, since it's turtles all the way down.

The fact of the matter is, if someone boots up a brand new linux laptop
goroutine switching _will_ be faster than thread switching. That doesn't mean
Go is super crazy performant, or better than C++ or Rust, it means someone
who's only programming experience is Rails apps can now write performant multi
threaded code with orders of magnitude less domain experience. Same with race
detectors. Multithreaded Python is a complete minefield for race conditions.
Sure, you might not segfault, but you have to think if you use fork() or
spawn() depending on the OS, you have to install libraries to detect races,
you have to write special tests. With Go all of that comes out of the box and
it makes it _easy_.

Go does nothing new, and a lot of languages do things a lot better. Erlang is
mentioned in other comments, Erlang is a fantastic language for concurrency
and a fantastic language in general. It's also incredibly hard to find
programmers who code in it, or are willing to learn it. It's also incredibly
hard to sell to the business people higher up. It's also very hard to find Ops
people who can competently support an Erlang stack. C++ gives you the power to
build formally correct real time systems, but it also gives you the power to
blow your whole leg off if you don't know _exactly_ what you're doing. I can
go even further down the stack with C and assembly but I think you get the
point, it's all about tradeoffs. Go allows programmers to reason and think
about concurrency without having to worry about linux kernel PRs, without
having to worry about how to share memory, without having to worry about stack
performance.

*edited for clarity

~~~
lossolo
> but your perspective is totally off.

Well, his perspective is well known in all Go threads on HN and this is not
only my opinion (look here [1]). He is repeating the same things[2] again[3]
and again[4] and again. About M:N in Go, about why Go is worse because it
doesn't have generational GC but when asked if he reached to Go team about
that there is no response. If you look at his comments from last month you
will see how much downplaying of Go is there + other languages. It seems that
he only praise one language (ahem) in his comments.

1\.
[https://news.ycombinator.com/item?id=17886153](https://news.ycombinator.com/item?id=17886153)

2\.
[https://news.ycombinator.com/item?id=18101986](https://news.ycombinator.com/item?id=18101986)

3\.
[https://news.ycombinator.com/item?id=17886144](https://news.ycombinator.com/item?id=17886144)

4\.
[https://news.ycombinator.com/item?id=17886122](https://news.ycombinator.com/item?id=17886122)

~~~
steveklabnik
> It seems that he only praise one language (ahem) in his comments.

So, I've seen this pop up a few times, but I'd also like to make this really
clear: Patrick formally stepped down from working on Rust a year and a half
ago, and was inactive for a while before then. At this point, he's the same as
any other user.

That is to say nothing about my opinions about his opinions, but let's be
clear, rather than insinuating things: Patrick speaks for himself, not for the
Rust team.

~~~
lossolo
Thanks for clarification, I didn't know about that. That doesn't change
anything I wrote but it's good to know this was not coming from current member
of the Rust team.

------
ken
> I'm happy to go on record claiming that Go is the mainstream language that
> gets this really right. And it does so by relying on two key principles in
> its core design...

The unmentioned third principle that it relies on is: "Curly braces, so it
looks almost like C if you squint". That's what makes a language "mainstream"
these days.

It looks like Go is very good at concurrency, but from everything I've read, I
don't see how it's any better than Erlang or Clojure. The only controversial
part of Eli's claim is the implication that other languages that get
concurrency right aren't "mainstream". That's not a well-defined term and so
naturally this is going to irk many people.

Perhaps the title would have been more accurate as "Go hits the concurrency
nail on the head, using the hammer of K&R style". :-)

~~~
virmundi
Go doesn’t need a VM. That’s key. You get good concurrency with an easy to
deploy binary that can target the major chips.

~~~
ken
That's a good point. Modern programming languages are all pretty big and
complex and whenever someone tries to nail down " _this_ is why it's
good/popular" there always seem to be other significant factors that got
missed. After all, if it were just one factor that leads to programming
language popularity, we could design the Next Big Language by just following
that recipe!

In the case of Go, I can imagine many of its attributes are significant:

\- Good concurrency model

\- Familiar style for imperative C/Algol-family programmers

\- Requires no VM

\- Backed by major corporation

\- Runs on all major OSs

\- etc

Actually, I think I'm changing my mind. I'd put "corporate backing" higher on
the list. Some languages have gotten a huge boost by being backed by a major
corporation (classic example: Objective-C), and I'm having trouble thinking of
a general-purpose programming language backed by a major company that did
_not_ become popular (Dart would be my best guess but even that seems to be
doing alright).

~~~
kureikain
> Backed by major corporation

I don't think Go success has anything to do with Google. Google engineers
probably favors Java/Python.

I think the key thing to make Go adopt is because the Docker project and Hashi
Corp.

The second most important thing is it is super easy to compile Go program and
run it and adopt by DevOps teams

~~~
tmerr
Eh? I can't speak for all of Google but I like me some Go. It has a culture of
minimalism both in language and code, which makes it easy to pick up and read
others' work. I wouldn't recommend it for all problems, but it's at least nice
for backend services.

Java is a victim of its own success: it has lived through style evolutions
which have lead to fragmentation in the ecosystem. Some code uses mutable
datastructures with for loops, others immutable datastructures with the stream
API. Some code uses dependency injection, while some doesn't. As a result it
takes more time to mentally calibrate to the style when pulling up a .java
file. Another imperfection: GC pauses can cause request timeouts and are
annoying to debug.

Python's not perfect either: aside from the obvious performance issues, use of
the awkward retrofitted type system makes me wonder why a statically typed
language isn't being used to begin with. The lack of good static analysis is
especially annoying at Google where the tooling is quite good.

Just my 2c

~~~
apta
> it has lived through style evolutions

The same can be said for C++, C#, etc. that have been around for a long enough
time. Golang is relatively speaking still new. Look back at this again if
golang adds generics and other features that will change how code in it is
written.

> Another imperfection: GC pauses can cause request timeouts and are annoying
> to debug.

That's not an issue with Java, but how the GC is tuned. Golang provides only 1
gc that is tuned for latency. And even that is not hard real time. The JVM has
several GCs you can choose from, including latency optimized ones.

~~~
tmerr
>Look back at this again if golang adds generics and other features that will
change how code in it is written.

Absolutely. I was surprised by the number of proposed changes in Go 2,
especially regarding error handling. Curious whether old code will be migrated
or left behind.

>That's not an issue with Java, but how the GC is tuned

Good point, I haven't done much GC tweaking personally but that sounds worth
learning more about. However I think language might have _something_ to do
with it: In Java almost everything is a referenceable object, while in Go one
can put values directly inside structs. So Go has less work to do. Without
that advantage I doubt it would seem competitive with Java GCs.

~~~
apta
Java will be getting value types in an upcoming version. C# already has value
types (called structs). The JVM also does escape analysis to avoid heap
allocation when it can.

------
vvanders
> Programming with threads is hard - it's hard to synchronize access to data
> structures without causing deadlocks; it's hard to reason about multiple
> threads accessing the same data, it's hard to choose the right locking
> granularity, etc.

It's almost as if we need a language that has a focus on data races,
concurrency and fearless threading.

~~~
rubiquity
Hah! Good one. To anyone else not in on the joke, parent is referring to the
language that is often discussed on HN and known for having a sometimes overly
enthusiastic community, Pony:
[https://www.ponylang.io/](https://www.ponylang.io/)

~~~
eatbitseveryday
Hah! You must be in on another joke, because OP was referring to Rust ;)

> Rust is a systems programming language that runs blazingly fast, prevents
> segfaults, and guarantees thread safety.

www.rust-lang.org

edit: /s

~~~
nemothekid
Wooosh

Edit: I made this post, but I thought afterwards, what if you were you being
dense on purpose, and I didn't get the joke?

~~~
cube2222
I think this thread is a nice reminder that it's really helpful to use /s when
you're using sarcasm in written form.

