Live-Coding Environment for Clojure
Session lets you explore ideas and execute computations, with minimal incidental complexity. It stores your interactions so you can come back to your work later, or share it with others. And its architecture allows tight integration with production environments, so you can easily move between exploration and production.
Session is most similar to the notebook environment pioneered by Mathematica, and increasingly popularized by IPython. It provides visual rendering of output, and allows one to build up a history of work.
However, Session seeks to solve the problems I’ve encountered over years of working with the notebook, and is not a notebook system. It is a different kind of architecture, incorporating modern ideas about state management, data rendering, and interoperability.
Unlike other environments, Session is not a closed system. The two most crucial boundaries of the system — storing data, and rendering it — are handled by data-oriented technologies that are not Session-specific. Data created and consumed by Session is readily integrated with other applications, both backend and user-facing. There isn’t a rigid boundary between Session and the outside world.
Session also provides a principled foundation for managing state, based on ideas from Clojure and Datomic. State management is crucial to live-coding, yet has been traditionally overlooked, resulting in countless bugs and usability issues.
Three Big Ideas
There are three big ideas underpinning Session.
The first is backing the live-coding environment with a sound information model, based on immutability, that tracks the entire history. This idea is realized by using Datomic to store Session’s data.
The second is the idea of tangible values: the idea that immutable data can be used to represent graphical or UI constructs, can be immediately rendered with no additional wrapping or ceremony, and manipulations by the user are directly reflected in the underlying data structure. This idea is realized by using Om to render Session’s data.
Combining these first two ideas with the notion of a REPL gives us Session. The user enters Clojure code into Session, it is sent to the backend for evaluation in the JVM, data is returned to the browser, and rendered in context together with previous results. The entire interaction is stored in Datomic.
The third idea is more of a philosophy than a technology: Simplicity. Session is simple, in the Rich Hickey sense; it is fundamentally not intertwined with itself. Its components can be independently reused in different contexts. Session’s renderers can be used outside of Session; Session’s data can be used outside of Session. Clojure, Om, and Datomic are very flexible, and won’t thwart unforeseen use cases. Data is what ties the system together.
Session: Characteristics and Capabilities
Session’s information and rendering models are what ultimately determine its computational characteristics. As outlined in the subsequent section, important implementation work remains to fully realize them. But only by understanding the consequences of a design can we see if it has the potential to solve our problems.
Datomic provides Session with a number of desirable attributes. (For an overview of Datomic, watch this video)
- A history of all interaction. You can rewind your interaction to any previous state; it is not possible to accidentally lose information. Session effectively inherits infinite undo from Datomic.
- A powerful query model. You can query all your inputs and outputs across your entire history (for instance to find the version of a function that performed best on some benchmark.)
- An interface to other systems. Datomic is a general purpose database, available to any other application concurrently with Session. Other processes can be inserting new data, or querying the database for content to send to other systems. The database itself is an immutable value, and can safely be conveyed as a whole.
- A mechanism for meta-data and resource management. The location of data and files, the dependency relationships between computations, and any relevant environment information can in principle be stored in datomic and used to automate away incidental complexity.
- A rich database for general use, built into Session. Your code has immediate access to commonly needed, powerful functionality.
Session’s user interface is based on Om, a web technology based on Facebook’s React for rendering data into interactive components. Om provides Session with the following attributes:
- Composable, declarative UI and graphics constructs. Creating graphics and UI is as easy as generating and manipulating data. No wrappers or stateful manipulation, just plain old functional programming.
- A model extensible down to the foundation. Om provides a modular, easy-to-understand model for creating new declarative constructs.
- Exportable content. Any other application can use Om to render content created in Session.
- An ecosystem of components. As the Om ecosystem grows, so too will the constructs and capabilities of Session.
Building the Future of Session
Session already achieves much of what I set out to do in building a new kind of live-coding environment, but there is still much to do to realize the full vision.
- Usability. Session is currently very alpha. My focus and primary goal is to make it a day-to-day usable tool.
- The State Model. This is the big enchillada. Despite its ability to track everything, Session still allows inconsistencies between what is displayed to the user and the actual state of the system. This is a problem at the intersection of UI design and programming language semantics, and complicates seemingly basic things like importing exisiting Clojure libaries.
- Location independence for data. We need to get away from manually managing the location of datasets. Between datomic and clojure, we can make the actual location of data transparent.
- Components with server-side behavior. Declarative UI constructs currently only have client-side behavior. We need an extensible model to allow them to have server-side counterparts.
- Global repository/stream of components and computations. We should leverage the database-nature of Session to built indices and sharing mechanisms. User-defined UI constructs need to be automatically managed so we can see each other’s work.
Contributing to Session
While there are still thorny design problems to be tackled, there is an immediate way to help grow Session today: get involved with Om, and create Om components.
Om components can be easily incorporated into Session, and a healthy Om ecosystem translates into a powerful Session.
A great place to start would be a better paredit-style editor widget ☺
Also, try out Session and let me know what you think!