- Transactional Memory
- Functional Languages and Monads
- Message Passing and the Actor Model
- Map Reduce
- No Silver Bullet
Functional Languages and Monads
Functional languages have been promising concurrency for decades, but only recently has anyone actually cared. A pure functional language has no side effects; every pair of functions in the same scope can be executed in parallel.
This doesn't sound very useful. Side effects are the reason we run programs. A program that performs no I/O is a waste of processor time. Some support for side effects is therefore added to functional languages, via a variety of mechanisms.
One such mechanism, popular in Haskell, is monads. A full explanation of monads is well beyond the scope of one article; entire books have been written trying to explain the concept. The easiest way I've found to think about themwhich I'm reliably informed is misleading and wrongis as an external lambda term containing all of the stuff that isn't allowed into a pure functional model.
When you write a program with monads, you use them to collect side effects outside of an expression. More interestingly, they provide implicit ordering. This is the exact opposite of concurrent programming in an imperative language. If you write parallel code in C, you write code that's intrinsically serial, and then you add some extra parallelism. In Haskell, you write code that is inherently unordered (and therefore potentially parallel), and implicitly define the order of some subset of operationsones that require orderingvia monads.
A monad collects a sequence of computations. Monads work especially well in conjunction with the transactional model. It's no surprise that the Glasgow Haskell Compiler is where a lot of the most interesting research involving both monads and software transactional memory takes placestransactional memory in Haskell is implemented in terms of monads.
The advantage of the monad approach is that it automatically supports an undo operation on functions with side effects. Just like memory operations, the side effects are committed only if the transaction succeeds. This design lets you use transactions that do things like write to sockets or display things to the user, but perform the output only if the entire operation actually succeeds.
Input is much harder. It must be buffered, and the only way you can undo input is to push it back to the buffer. You can't arbitrarily interleave input and output inside a transaction, because a later input may depend on an earlier output.