Friday, March 14, 2014

Concurrent ML and HOPAC

One of these days I got a hold of a copy of Concurrent Programming in ML by John Reppy, an excellent book. One of the many virtues: it is the missing documentation piece for Concurrent ML (CML) system, explaining design motivations and giving helpful examples.

I wish it was in open domain. Why? Simply to push CML design further, bring more attention to it. It is so much better than what is being commonly used. This is not to say that there is no space for other concurrency abstractions. But the CML has the advantage of offering a small set of features from which all other useful abstractions are easily built. I cannot do a better job than the book for advocating CML, but I particularly like:

  • Simple to understand semantics - programs in PI or other process calculi map very closely to CML code
  • Makes it easy to build other abstractions - asynchronous communications, locks, buffers, reactive systems, you name it
  • Selective communication seems VERY important
  • Plays well with ML-style languages (my favorite)
  • Admits moderately efficient implementation - should frequently be good-enough for production, and definitely excellent for prototyping

In a tweet I mentioned that having the book in open domain would "help fight actor/reactive nonesense" and was asked to elaborate. So here are some statements I came to disagree with:

  • Concurrent software should be written exclusively with Erlang-style asynchronous message-passing
  • It should be written with reactive systems of RX/IObservable flavor
  • It should be written in the FRP paradigm
  • F# has excellent support for concurrency, it provides `async` and "actors"..

Obviously, Erlang-style message passing, reactivity, and carefully designed FRP systems (such as Elm language, AFRP) are all good for certain problems. RX/IObservable systems, IMHO, are a net liability. In general, take any of these paradigms as the default, and you will quickly find programs that you want to write fit the paradigm no better than a saddle would fit a cow. I think CML stands out as a much better foundational choice, subsuming the others.

Note to F# users - thanks to valiant effort by Vesa Karvonen, you can now use CML-style primitives in F#. See the Hopac project - it needs a bit more love!

Note to lovers of F# async. Async is a hack, but a good one. It is what you do when you want cheap by-the-million threads but do not have the time to rewrite the runtime (CLR). However, with a proper runtime, there is no need. See Racket or CML, where blocking syntax is used to orchestrate millions of lightweight threads.

Note on deadlock: CML-style channels use sync write, but async write is easy to write too. The book argues why sync writes are a better default. Yes you can program deadlock, but you can do the same within any sufficiently expressive concurrency paradigm. You definitely can in Erlang, try it as homework.