A Concurrent Language for Non-Concurrent Software

Occasionally I get asked why, as someone who uses Erlang extensively, do I rarely talk about concurrency?

The answer is because concurrency is not my primary motivation for using Erlang.

Processes themselves are wonderful, and I often use them as a way to improve modularity. Rather than passing the state of the world all over the place, I can spin off processes that capture a bit of it. This works surprisingly well, but it's just a coarser-grained version of creating objects in Python or other languages. Most of the time when I send a message to another process, my code sits and waits for the result to come back, which is hardly "concurrency."

Suppose Erlang didn't have processes at all. Is there still anything interesting about the language? To me, yes, there is. I first tried functional programming to see if I could think at a higher level, so I could avoid a whole class of concerns that I was tired of worrying about. Erlang is further down the purely functional road than most languages, giving the benefits that come with that, but at the same time there's a divergence from the hardcore, theoretical beauty of Haskell. There's no insistence on functions taking a single value, there isn't a typing-first viewpoint. The result is being able to play fast and loose with a handful of data types--especially atoms--and focus on how to arrange and rearrange them in useful ways.

(Okay, there are some small things I like about Erlang too, such as being able to introduce named values without creating a new scope that causes creeping indentation. It's the only functional language I've used that takes this simple approach.)

The angle of writing code that doesn't involve micromanaging destructive updates takes some time to sink in. Possibly too long; something almost always ignored when presenting a pathologically beautiful one-liner that makes functional programming look casually effortless. There are a number of techniques that aren't obvious, that aren't demonstrated in tutorials. I wrote about one in 2007. And here's another:

Lists in Erlang--and Haskell and Scheme--are singly-linked. Given a list, you can easily get the next element. Getting the previous element looks impossible; there's no back pointer to follow. But that's only true if you're looking at the raw definition of lists. It's easy if you add some some auxiliary data. When you step forward, remember the element you just moved away from. When you step back, just grab that element. The data structure looks like this:

{Previous_Items, Current_List}

To move through a list, start out with {[], List} . You can step forward and back with two functions:

forward({Prev, [H|T]}) -> {[H|Prev], T}. back({[H|T], L}) -> {T, [H|L]}.

Wait, isn't that cheating? Creating a new list on the fly like that? No, that's the point of being free from thinking about managing memory or even instantiating classes.

permalink October 17, 2010

previously