Philipp Meier - Software Developer

Abstracting the pain away

Homoiconicity Brings Pretty Graphs to Liberator

| Comments

homoiconic - Same representation of code and data, from homo meaning the same and icon meaning representation

DougMcIlroy & C.S.Peirce 1960 PeterDeutsch & CalvinMooers 1965 AlanKay 1969, C2-Wiki…

TL;DR - liberator source -> http flow chart in graphviz dot -> svg/png -> awesome

One of the key features of LISP is that it is homoiconic which, one could say, enables creative use of the source. The most prominent example are macros. Second, the generation of documentation from the the source is common practice even in heteroiconic languages but homoiconicity certainly makes it a lot easier. In this post I like to present a more exotic application: automatic generation of flow charts.

The liberator HTTP library provides a declarative way to implement HTTP resources conforming to the HTTP RFC. This is not as simple as it sounds, actually, RFC2616 is a large and complected piece of network engineering. To tame the beast liberator implements a decision graph containing a lot decision function callbacks which a developer can fill in or rely on a default implementation.

Based on this information the program flow takes a certain route and finally arrives at a handler which is responsible to build the actual HTTP response. The response will be based on the dervied status code, negotiated representation parameters as language and media type and so on.

For a more advanced use case consider a PUT a resource with a If-Not-Modified-Since-Header which returns a 302 see other. And there is more: content negotiation, conditional requests, checking for existance… The exact reason why a certain outcome was produced can be tricky to understand. This was the motivation to build a flow chart of the decision process, a feature that Erlang’s webmachine provides too. Webmachine actually was my inspiration to start what used to be called “compojure-rest” and now is liberator. You can find the flow chart for webmachine here.

And this is how it correlates with clojure’s homoiconicity. In the liberator code every decision is defined using a macro (defdecision name test then else) where “then” and “else” are names of other decisions. This actually looks like this:

We can then use a reader to read the source file that contains all the decisions (and actions, and handlers) into a list of lists and convert it into a Graphviz file:

Central idea is to “(read pr)” until no more expressions are available. Then map every expression to a string snippet which encodes one edge of the graph. To make this happen, to-graph looks for the two well known expressions (defdecision name then else) and (defaction name then) and convert them to an edge definition and some formatting.

The current implementation is more complete and formats “decisions”, “actions” and “handlers” differently. You can find it in the liberator source repository in test/doc.clj. For the current revision It produces this dot file (cut down to a reasonable size):

A rendered version of the graph looks like this:

You can access it in full size as png, svg and dot.