There are a lot of ingredients that might or might not go into the winning formula that brings concurrent programming to the mainstream. This is a very brief run-through of as many as I can think of.
[This is part of the Concur.next series. At the moment, I think the next few pieces are going to be discussions of some or maybe all of the items in the list. If you’ve published something particularly gripping about one of them or another, shoot me a link.]
I’ll try to update this piece lots; I’m sure people will write in to disagree with my characterization and to argue for the addition or removal of the items from/to the list. Since this is an enumeration rather than an opinion piece, I have quite a bit of hope that it might come to represent community consensus.
In this discussion, I frequently refer to the HECS (Haskell, Erlang, Clojure, Scala) languages. I am not claiming that one of these is the winner or that there aren’t other worthwhile contenders. It’s just that they keep floating to the top and (I think) represent an instructive range of ways to aggregate the features in this laundry list.
Functional Programming · Hereinafter FP, please. The Wikipedia explanation is fairly impenetrable. Erlang is a decent gateway drug and Haskell is definitely the hard stuff.
The proportion of a program which is composed of entirely side-effect-free function calls should by definition be arbitrarily parallelizable. That’s the theory, anyhow. Essentially every modern programming system that claims to address concurrency provides some FP capabilities.
Immutable Data · If your data objects are immutable, you can operate upon them freely and concurrently without fear of corruption or the need for locking. Plus you can use them as interprocess messages without actually copying them, or ship them between physical machines and know that the version you left behind is still current, whatever they do over there. It seems pretty obvious that this is a powerful tool for use in addressing concurrency problems.
There are gradations of immutability. You can have immutable variables and do what feels like changing them if you auto-magically make a timestamped new version while preserving the validity of pointers to the old one. There are all sorts of data-structure tricks you can do, for example an “immutable” array where you append an element, logically producing an entirely new object but in fact re-using the pre-append part of the array.
This is a simple notion but very deep subject.
Processes and Actors · It’s axiomatic that we’re not going to expose threads in the operating-system sense. But it seems that programmers ought to be allowed to see some notion of a sequence of operations that proceeds in parallel with other sequences of operations.
Erlang calls them processes, Scala calls them actors (there’s a big chunk of Computer-science theory at work there), Haskell doesn’t call them anything but the documentation bandies the term “Thread” about.
The way in which this is exposed to the programmer seems to me like a crucial defining characteristic in building a strategy to attack the concurrency problem.
In the context of this series, when I say “process” I’m using it more or less in the Erlang sense, as a sequence of execution with its own (logical) call stack and heap and so on, without asserting that its implementation is based on an OS process or thread, or that it is or isn’t an Actor in the formal sense.
Message Passing · If you’re going to expose processes and avoid global data, you need a way for them to communicate. There is a lot of semantic variations in the way different platforms do interprocess communication, but a superficial look suggests that some shared patterns are emerging; Scala, for example, consciously echoes Erlang in its approach.
Typing · Here we have single greatest religious divide among programmers, and it cuts right across this space. Thank goodness none of the candidates are weakly typed a la Perl. Erlang has unsurprising dynamic typing but no objects. Scala has inferred static typing applied to Java-flavored classes/objects/methods.
Haskell has stronger and more elaborate (static) typing than anything I’ve ever been near; in fact they push a lot of what would normally be considered an application programmer’s work down into the type system. They have things called classes (OK, typeclasses), but they’re a horse of a different color.
It’s not obvious to me that the choice of type system is that important in building the Java of concurrency, but I could easily be wrong on that.
Virtual Machine · Is it an advantage to have your own virtual machine, like Erlang, to be based on another like Clojure and Scala, or just to compile to native code like Haskell? And the JVM in particular brings along a mind-bogglingly huge amount of existing software and libraries you can call out to. Well, except for a large proportion either isn’t optimized for concurrency or just won’t work that way at all.
The right answer here isn’t obvious at all.
Transactional Memory · Since nobody’s ever shipped hardware transactional memory, we’re talking STM here; Clojure in particular makes a big deal of this.
The core idea is sort of like applying ACID database semantics in accessing program variables. The effect is that you can mutate things concurrently where you need to in the context of a transaction; if a collision occurs, the whole transaction is rolled back and inconsistency doesn’t arise.
Maybe so, but it’s sure not the present; I’ve never actually had my hands on a deployed piece of software that relied on a tuple space. And at the moment, I don’t hear anyone claiming this is the way to build the Java of concurrency.
Dataflow · But, like tuple spaces, Dataflow is an idea that looks like it ought to be real useful in building concurrent systems. Concretely, you ought to be able to farm out the recalculation of a big complex spreadsheet to a bunch of processors until you get to the last-step sum computations.
Like tuple spaces, I don’t see anyone trying to build the concurrent future with dataflow techniques.
Reliability · This is only here because of Erlang, which claims to have two design goals: First, to enable concurrent computation, and second, to make such computation reliable in the face of software and hardware errors. In order to accomplish this in a process-based paradigm, it wants to handle all errors simply by blowing up the process where they happened; then there’s a really slick system of local and remote process monitors that should enable you to keep your system on the air in the face of a whole lot of different classes of problems.
The thing is, once you’ve worked with Erlang a little bit, the notion of trying to deliver any concurrent system without those sorts of monitors and failovers and so on begins to be seriously worrying. My personal bet is that whatever we end up with is going to have to have a good story to tell in this space.
Language or Library? · This is a big one. Do we need a whole new platform? A new language on an existing VM? Or maybe even just a set of libraries you can use from existing languages?
There are Actor libraries that I know of for Java, Ruby, and probably essentially every other modern language. And nobody’s forcing you to make your variables mutable or to share data between threads. Or even to use threads; in my own Wide Finder project (I, II), there was no real evidence that thread-level concurrency outperformed processes.
These days, given the choice, I prefer to code in either Ruby or Python. I’d love to be able to keep as much of that lightweight dynamic enabling goodness as possible and still drink from the fountain of concurrency.
Distribution · Which is to say, can your concurrent application code run across multiple physically separated computer systems, as opposed to just the cores on one system? If so, is it automatic or under the programmer’s control. Erlang makes this explicit, as a reliability mechanism, but for problems that get really large in scale, this could easily become a gating limit on performance.
What’s Missing? · From this laundry list, I mean. Or what’s really wrong and misleading. Or should any of these be cast into the outer darkness?