Skip to main content

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
  • Serve

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.

Footnotes:

There are a lot of things related to what I'm talking about such as Flow-based programming, the Actor model, etc. On the other hand, perhaps there are no silver bullets.

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 ;)

Comments

proppy said…
is GNU `make` allowed? :)
pinacolada: serve
@echo piña colada
serve: get_glasses get_pink_umbrellas open_blender_again
@echo serve
open_blender_again: liquefy
@echo open blender again
liquefy: close_blender
@echo liquefy
get_glasses:
@echo get glasses
get_pink_umbrellas:
@echo get pink umbrellas
close_blender: put_mix_in add_two_cups_ice pour_in_rum
@echo close blender
put_mix_in: open_mix open_blender
@echo put mix in
add_two_cups_ice: open_blender
@echo add two cups ice
pour_in_rum: measure_rum open_blender
@echo pour in rum
measure_rum:
@echo measure rum
open_blender:
@echo open blender
open_mix:
@echo open mix

$ make -j -f pinacolada.make
get glasses
get pink umbrellas
open mix
open blender
measure rum
put mix in
add two cups ice
pour in rum
close blender
liquefy
open blender again
serve
piña colada
jjinux said…
Nicely done, proppy!
Paddy3118 said…
Hi, you seem to have two processes with the same name: "Open blender".

You probably want to use two names, maybe:
Open blender when empty
and:
Open blender when mixed.

Cheers, Paddy

Popular posts from this blog

Drawing Sierpinski's Triangle in Minecraft Using Python

In his keynote at PyCon, Eben Upton, the Executive Director of the Rasberry Pi Foundation, mentioned that not only has Minecraft been ported to the Rasberry Pi, but you can even control it with Python . Since four of my kids are avid Minecraft fans, I figured this might be a good time to teach them to program using Python. So I started yesterday with the goal of programming something cool for Minecraft and then showing it off at the San Francisco Python Meetup in the evening. The first problem that I faced was that I didn't have a Rasberry Pi. You can't hack Minecraft by just installing the Minecraft client. Speaking of which, I didn't have the Minecraft client installed either ;) My kids always play it on their Nexus 7s. I found an open source Minecraft server called Bukkit that "provides the means to extend the popular Minecraft multiplayer server." Then I found a plugin called RaspberryJuice that implements a subset of the Minecraft Pi modding API for B

Ubuntu 20.04 on a 2015 15" MacBook Pro

I decided to give Ubuntu 20.04 a try on my 2015 15" MacBook Pro. I didn't actually install it; I just live booted from a USB thumb drive which was enough to try out everything I wanted. In summary, it's not perfect, and issues with my camera would prevent me from switching, but given the right hardware, I think it's a really viable option. The first thing I wanted to try was what would happen if I plugged in a non-HiDPI screen given that my laptop has a HiDPI screen. Without sub-pixel scaling, whatever scale rate I picked for one screen would apply to the other. However, once I turned on sub-pixel scaling, I was able to pick different scale rates for the internal and external displays. That looked ok. I tried plugging in and unplugging multiple times, and it didn't crash. I doubt it'd work with my Thunderbolt display at work, but it worked fine for my HDMI displays at home. I even plugged it into my TV, and it stuck to the 100% scaling I picked for the othe

Creating Windows 10 Boot Media for a Lenovo Thinkpad T410 Using Only a Mac and a Linux Machine

TL;DR: Giovanni and I struggled trying to get Windows 10 installed on the Lenovo Thinkpad T410. We struggled a lot trying to create the installation media because we only had a Mac and a Linux machine to work with. Everytime we tried to boot the USB thumb drive, it just showed us a blinking cursor. At the end, we finally realized that Windows 10 wasn't supported on this laptop :-/ I've heard that it took Thomas Edison 100 tries to figure out the right material to use as a lightbulb filament. Well, I'm no Thomas Edison, but I thought it might be noteworthy to document our attempts at getting it to boot off a USB thumb drive: Download the ISO. Attempt 1: Use Etcher. Etcher says it doesn't work for Windows. Attempt 2: Use Boot Camp Assistant. It doesn't have that feature anymore. Attempt 3: Use Disk Utility on a Mac. Erase a USB thumb drive: Format: ExFAT Scheme: GUID Partition Map Mount the ISO. Copy everything from