The gofor macro

I've started working on a series of open source library code for Clojure called Dragonmark that roughly falls into three categories: utilities, a distributed CSP library, and a sample web app that demonstrates distributed CSP.

So, why?

Mostly, I think that the semantics for interprocess communication should be the same as the semantics for local communication. Clojure's core.async library provides a really nice set of APIs to communicate asynchronously, have backpressure, and in general "do the right thing."

But core.async does not provide any interprocess communication. In fact, nothing in Clojure-land does a good job of abstracting the transport layer and maintaining the semantics of core.async.

So, Dragonmark.

Making core.async less verbose

If you're going to do core.async right, there's a pattern... create a channel to receive a reply, send a message, wait for a timeout or an answer, close the channel, if the answer came from the channel, do something, else, do an error.

Using Scala and LAFuture is really easy in a for comprehension. I wanted something similar in Clojure-land, so I wrote the gofor macro.

Here's an example:

(gofor
  [a (foo channel {:thing value :other_thing other_value})]
  :let [b (+ a 1)]
  [c (baz other_channel)
   d (moose third_channel {:p b}
   :timeout 45000]
  (println a b c)
  :error (println &err &at))

The [a (foo channel {:thing value :other_thing other_value})] sends a message to channel with {:_cmd "foo" :thing value :other_thing other_value :_return a_created_channel} and waits up to 30 seconds for an answer. If the answer doesn't arrive, the :error code is invoked and the comprehension is terminated. If the answer does come back, it's bound to a.

Next, we bind b to (a + 1).

Next, we kick off a parallel set of messages to other_channel and third_channel and wait up to 45 seconds for a response.

If we succeed, the (println a b c) code is executed.

It's a first pass at something that makes core.async more usable, especially in the case of cross-address space messaging where timeouts and failures should be expected and embraced.