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.
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
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
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
When to use single- versus double-bang (!)
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:
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.
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.
put! to get stuff into your channels from outside.
put! to get the value onto a channel.
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.