Last Wednesday, I gave a short talk at Baltimore Innovation Week DevDay, entitled “Time and Concurrency”. The slides are hosted here, but I’ll try embedding them:

Okay, that seems to work.

I won’t recap the entire talk here, as I’m sure I’ll be presenting (a revised version of) it again in the future, but I’ll summarize.

Time

Programmers, especially in the established object-oriented (and imperative) paradigms, have a problem with time. This isn’t a new argument; Rich Hickey spends most of his talk Are We There Yet? problematizing time in object-oriented programming; even Uncle Bob (Robert C Martin) addresses this in Functional Programming; What? Why? When?.

I wanted to present the problems with time and concurrency, which are really problems with state, value, and identity, and how functional programming, specifically using Clojure and Datomic, allowed us to either negate or embrace these problems.

Put simply, mutable state is evil. Sometimes a necessary evil, but evil nonetheless. Even something as simple as assignment:

foo = 3
puts foo
=> 3
foo += 1
puts foo
=> 4

foo is not the foo I started with! We create an identity, foo, assign it a value, and then willy-nilly assign it a new value. Consider:

(def foo 3)
(println foo)
=> 3
(println (inc foo))
=> 4
(println foo)
=> 3

The function inc (increment) doesn’t alter foo, it simply takes a value, and returns a new value. This is because inc is a pure function; it has no side-effects and cannot alter state.

The problem is that, as Rich Hickey puts it, “object-oriented languages have no reified notion of time”. Objects change over time, but we allow them a constant identity and have no guarantees (beyond mutexes and locks, which are the gateway to madness) to ensure that foo has the value I expect when I dereference it.

Pure functions have no concept of time, because having no state, time is not an issue; they take values and return values, always the same, every time.

Concurrency

Way back when we only had to consider single-threaded, single-process programs, time did seem very linear. But in a world of multiple threads in multiple processes spread across multiple cores, and often multiple machines, running in parallel, shared mutable state is a huge problem. Pure functions return the same result, given the same inputs, anywhere they are run, and don’t alter anything. Clojure’s emphasis on immutable state, persistent data structures, and pure functions1. In Clojure, mutable state is managed, thread-safe, and mutations are always explicit, usually within a transaction.

Datomic

Datomic takes the ideas of time and immutability and applies them to the database. Datomic is an EAVT tuple-store, supporting both ACID transactions and unlimited horizontal read scalability. I highly recommend watching Rich Hickey’s talk Deconstructing the Database for a deep look at the theory and practice behind Datomic. But in borad strokes, instead of storing tables of mutable rows and columns (SQL style), or mutable documents2, Datomic stores facts. These facts are immutable; if something changes, a new fact is inserted, or a previous fact is retracted. That insertion or retraction is stored as a new fact.

This allows Datomic to apply the concept of time to the database; the previous state of the db as-of any point in time can be queried, and since the database functionally uses the same persistent data structures as Clojure, external data can be queried alongside Datomic data as if it were in the database. This concept of time allows us not only to examine any past state, but to virtually query the future by adding hypothetical data to our queries.

I won’t go into the mechanism of how this all of this is achieved, rather I’ll direct you to the videos on datomic.com.

DevDay

DevDay Talks was a really great experience. You can see the full speaker lineup on the BIW site (linked above). I especially enjoyed Eric Oestrich’s talk on Kubernetes, and Richard McCluskey’s dive into DevOps at Millenial Media. So many great talks, and the after party was a lot of fun too. Thanks to Baltimore Innovation Week and Technical.ly Baltimore for throwing such a great event!

  1. “Impure” functions, i.e. those having side-effects, are allowed in Clojure (unlike, say, Haskell), but are the exception rather than the rule

  2. Like, e.g., MongoDB or CouchDB