Willy Wonka and the core.async Guidelines
Summary: There are a few conventions in core.async that are not hard to use once you've learned them. But learning them without help can be tedious. This article presents three guidelines that will get you through the learning curve.
Introduction
The more you use core.async, the more you feel like Willy Wonka. He knew how to maximize the effectiveness of the Oomploompa. And while core.async comes with a lot of functions built in, he knew exactly which ones to use at which time.
In this extremely rare glimpse into the functioning of his mysterious factory, we take a look at the guidelines Wonka himself follows when orchestrating the work of the Oompaloompas.
When to use go
versus thread
?
Willy Wonka with his Thread Pool.
Background
Each Oompaloompa is a thread. Willy Wonka has a special group of
Oompaloompas he calls a thread pool. Their assigment is simple: they
manage a group of tasks that Wonka calls go
blocks. Whenever Wonka has
an appropriate task, he writes a go
block and hands it to the
Oompaloompas to work on.
As the Oompaloompas work, they take one task and do it until the task parks. When it parks, they put it down and pick up another task that isn't parked. Tasks become unparked when they get new input from the chocolate pipes. Then the Oompaloompas can continue working on them.
At one time, Wonka used to give the thread pool all sorts of tasks. He would give them very long calculation tasks, like weighing each chocolate bean in his chocolate bean mountain. He noticed that when they did this, lots of tasks were left undone, even though they were not parked, because all of the Ooompaloompas were busy doing something else.
So he came up with a guideline.
Avoid long calculations and blocking inside go
blocks
Does your code do significant I/O, like downloading a file or writing to the network? Are you doing a very long calculation?
Then use a thread
. If it will take a long time or block, you want a
dedicated thread. It can work as long as it wants, and even block.
That way it doesn't slow down the work of the thread pool.
Otherwise, you can use a go
block.
When to use single- versus double-bang (!)
Background
Wonka also noticed that he needed to write different instructions
for his two types of Oompaloompa. When he wrote a go
block, he needed
to say "park while you wait for input". But for the other Oompaloompas
created with thread
(or for his own work), he needed an instruction
that said "block while you wait for input".
So he came up with a little notation convention. If you're just
parking, so you're in a go
block, use one bang. If you're outside of
a go
block, meaning you need to block, use two bangs.
These were his versions of his basic instructions:
>!
, <!
, and alts!
versus >!!
, <!!
, and alts!!
. The
convention is easy.
Use single-bang versions in go
blocks and double-bang versions outside.
The single-bang versions of these functions are meant to park a go
block. Although they are defined as functions, they have special meaning
to the go
macro. In fact, if you actually run the functions (outside
of a go
block), they will throw an exception unconditionally, telling
you they are meant to be inside a go block.
The double-bang versions are blocking. That means that the thread they
are running on will block if the channel is not ready. They can be used
outside of a go
block (anywhere) or inside of a thread
block.
It's safe to block inside a thread
block since it's a dedicated
thread.
put!
Background
Like all factories, Willy Wonka's needs deliveries. When the UPS truck comes, there's plenty of boxes to unload. But Wonka is busy. So he leaves a note outside for the delivery guy.
The note tells the guy where to put everything so the Oompaloompas know
where to find it. When he says where to put a box, he spells it put!
.
That is, it has a bang.
It's unfortunate because the other functions with a bang mean they
park. But put!
does not park. Wonka was just angry one day, and the
convention stuck.
But the delivery guy knows that Wonka is eccentric, so he doesn't take it personally and does his job. He puts stuff in its places, without blocking.
Use put!
to get stuff into your channels from outside.
put!
is a way to get values from outside of core.async into
core.async without blocking. For instance, if you're using a
callback-style, which is very common in Javascript, you will want to
make your callback call put!
to get the value onto a channel.
Conclusion
That's it! Now to eat some chocolate!
core.async is really cool, but it has a learning curve. Once you learn these conventions, you will begin to feel the power they give you, whether you're making chocolate or building cars. If you'd like to learn core.async and feel like Willy Wonka, I recommend the LispCast Clojure core.async videos. They build up a deep understanding of the fundamental concepts in a fun and gradual way.