clj-worlds is an implementation of Alex Warth's "worlds" in Clojure.
The idea is explained nicely in this paper.
Worlds support scoped side-effects. In Clojure, they can be conveniently modelled as a special kind of ref, called a "world-ref" or w-ref for short.
A w-ref always has a value that is relative to the current world. Worlds form a single-rooted hierarchy, and a world can "commit" all of its changes to its parent world.
The API of clj-worlds is heavily inspired by Clojure's own
Like refs, w-refs can be created (
w-ref), dereferenced (
w-ref-set) and altered (
(let [w (sprout (this-world)) ; w is a child of the top-level world r (w-ref 0)] ; r is a world-ref (w-deref r) ; in top-level world, r is 0 (in-world w ; in world w... (w-deref r) ; ... r is also 0 (w-ref-set r 1)) ; ... we set r to 1 (w-deref r) ; back in top-level world, r is still 0! (commit w) ; commit all of w's changes to its parent world (w-deref r)) ; now, at top-level, r is 1
Comparison with refs
What is the difference with Clojure's refs and STM, you ask? Like transactions, worlds isolate and group side-effects. Unlike transactions:
Worlds are first-class entities: worlds can be created, passed around, and "opened" and "closed" at will. Committing a world's changes is an explicit operation and is not tied to the control flow of a block.
Worlds are not a concurrency control mechanism. World commits are not atomic, and while individual world operations are thread-safe, worlds do not support multiple atomic updates and thread isolation.
Integrate w-refs with regular refs. Make: see worlds_v1.clj.
- Experiment with stronger consistency checks. Using the default semantics of worlds, worlds can see inconsistent world-lines when other worlds commit to their parent. A possible fix is to introduce snapshot isolation, as for example employed by MVCC (which also happens to lie at the basis of Clojure's STM).