Making a Piña Colada in Haskell: It's All About Concurrency
I was reading The Pragmatic Programmer this morning, and it got me to thinking about Haskell.
Consider the following "function" for creating a piña colada:
- Open blender
- Open piña colada mix
- Put mix in blender
- Measure 1/2 cup white rum
- Pour in rum
- Add 2 cups of ice
- Close blender
- Liquefy for 2 minutes
- Open blender
- Get glasses
- Get pink umbrellas
It's very easy to understand and very linear.
Now consider the following diagram that conveys which parts can be done concurrently:
This description of the recipe is quite a bit more complex, but it's a lot more obvious which things can be done concurrently.
There are a lot of approaches to concurrency. For years, we've relied on our CPUs to give us some implicit concurrency. The CPU can look at the code at a very micro level and figure out which assembly instructions can be done concurrently because they're working with different parts of memory, etc.
Threads and processes also provide concurrency, but they're at a very different level, and it's very far from implicit.
Node.js also provides concurrency. However, telling Node.js which things can be done concurrently while responding to a request still takes a lot of careful thinking. You don't have to do anything to get Node.js to handle multiple requests at the same time. However, if you need to make three REST calls in order to respond to a particular request, it's up to you to notice whether or not you can do those calls concurrently, and if you do decide you can do them concurrently, it still takes some explicit coding to make it happen.
One of the essential problems is that it takes work to get from that nice linear description of how to make a piña colada to one in which all the opportunities for concurrency are explicitly stated. That's what got me thinking about Haskell again. Maybe laziness isn't such a bad idea after all ;) In Haskell, it's a lot easier to separate describing the steps necessary to do something from actually taking those steps. You can describe the steps in a way that makes sense to you, but let Haskell decide at runtime what order to take those steps in. Admittedly, I'm hand waiving a lot, and I haven't actually read Parallel and Concurrent Programming in Haskell, but I just have this feeling that Haskell makes you think in a way that the concurrent description of the recipe above will just kind of happen naturally.
Here's a fun exercise for the reader. Write a program that implements each of the steps above by doing a REST call. For instance, to "Open blender", just do a post to some server with the string "Open blender". The server doesn't really have to do anything (we'll just pretend). Implement the entire recipe using the opportunities for concurrency provided by the diagram above. Can your system figure out what things can be done concurrently automatically and then do them concurrently automatically? Bonus points if you can you make multiple piña coladas at the same time. Double bonus points if you can write code that is somewhat readable by someone unfamiliar with your system.
I'm not a real Haskell programmer or a Node.js programmer.
In fact, I've never even had a piña colada, so I could be way off base here ;)