<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Life is a Graph</title>
 <link href="http://lifeisagraph.com/atom.xml" rel="self"/>
 <link href="http://lifeisagraph.com/"/>
 <updated>2011-07-07T19:03:31+02:00</updated>
 <id>http://lifeisagraph.com/</id>
 <author>
   <name>Jeff Rose</name>
   <email>rosejn@gmail.com</email>
 </author>

 
   <entry>
     <title>Semi-structured data and P2P graph databases</title>
     <link href="http://lifeisagraph.com/2011/07/04/p2p-graph-databases.html"/>
     <updated>2011-07-04T00:00:00+02:00</updated>
     <id>http://lifeisagraph.com/2011/07/04/p2p-graph-databases</id>
     <content type="html">&lt;p&gt;In a &lt;a href='http://lifeisagraph.com/2011/06/12/plasma-a-graph-query-engine.html'&gt;previous post&lt;/a&gt; I introduced the &lt;a href='https://github.com/rosejn/plasma'&gt;Plasma&lt;/a&gt; graph query engine that I&amp;#8217;ve been working on as part of my thesis project. With Plasma you can declaratively define queries and evaluate them against a graph database. The heart of the system is a library of dataflow query operators, and on top of them sits a fairly simplistic query &amp;#8220;language&amp;#8221;. (I put it in quotes because in a lisp based language like Clojure the line between a mini-language and an API gets blurry.) In this post I&amp;#8217;ll write a bit about why I think graph databases could be an interesting foundation for next generation P2P networks, and then I&amp;#8217;ll give some examples of performing distributed graph queries using Plasma. First I think it is important to motivate the use of a graph database though. While most of the marketing speak on the web regarding graph databases is all about representing social network data, this is just one of many potential applications.&lt;/p&gt;

&lt;h3 id='semistructured_data'&gt;Semi-structured data&lt;/h3&gt;

&lt;p&gt;Graph databases are interesting because they allow you to represent semi-structured data. For some of the original insight that led to this concept I recommend checking out &lt;a href='plasma/querying-semistructured-data.pdf'&gt;Querying Semi-Structured Data&lt;/a&gt; by Serge Abiteboul. In short, he argues that there are many instances where a well defined schema cannot be known in advance. Semi-structured data, he argues, has an irregular structure that is implicitly defined by the data. Imagine, for example, that you would like to create a next generation wiki that allows you to organize and relate information in arbitrary ways. Often times a wiki will start with little to no structure, but over time you would like to regularize and structure parts of the data to make it searchable, sortable, and queryable. In a relational system this kind of &lt;em&gt;evolution with the data&lt;/em&gt; is painful to impossible. Migrating from one schema to the next can be laborious and difficult, and often times you only know that you need to store a new kind of data at the moment the data is available. If at each such moment you have to re-design the schema and create a migration it will severely limit the evolution of the database structure. This is where I think graph databases will shine in the future. Think of them more like a file-system++, where you can gradually add more and more structure to your data, and at any time you can create new kinds of relations or start storing new types of data. As we form richer connections to each other over the Internet, I think this kind of structural information will become even more important.&lt;/p&gt;

&lt;h3 id='a_data_driven_p2p_substrate'&gt;A data driven P2P substrate&lt;/h3&gt;

&lt;p&gt;Currently P2P applications tend to operate in isolation. BitTorrent, Naptser, Gnutella, eDonkey and the other file sharing systems each live(d) in their own walled garden. There are many advantages to a system that provides a common substrate on top of which a variety of P2P apps can be built though. Besides the ability to make better use of network and compute resources, the major advantage is with regard to the data. If P2P apps/algorithms can query semi-structured data on a peer then we can create evolvable, content driven P2P networks. For example, peers can be clustered based on common interests, and bits of data from many peers can be gathered to create new views and new applications on existing data. In some ways this is similar to what the semantic-web is attempting to do: represent data in a machine readable fashion so we can create software to perform automated &amp;#8220;reasoning&amp;#8221; across the web. I just don&amp;#8217;t think any kind of top-down, global ontology is realistic or practical though. In the long term we can agree to use some common schemas to better share data, and for this the semantic web efforts will be very useful, but in the meantime I think most data is and will be semi-structured. Every community, application and user will have their own ways of thinking about and relating information, and this needs to be taken into account. Furthermore, I want to live in a world where you have full control over your own data, and everything you care about can be local.&lt;/p&gt;

&lt;p&gt;The cloud is great for many applications, especially big-data processing, but it also has many disadvantages that people tend to gloss over. For one, the speed of light is not going to increase with Moore&amp;#8217;s law. No matter what we do cloud based systems will put hard limits on the latency for accessing data. In many instances this doesn&amp;#8217;t matter, but for creating collaborative applications where we are editing media or hacking on code, I don&amp;#8217;t think it will cut it. (Sure, some companies will have the resources to distribute replicas to nearby datacenters around the world, but do we want to limit ourselves to a few applications only offered by cloud-based mega corps?) More importantly, there are serious privacy and censorship problems with cloud based systems. I tend to trust Google, (and Facebook slightly less), but I still don&amp;#8217;t like the idea that they have access to all of my personal communication and social network in plaintext. In the west these issues are currently more of a moral/intellectual conundrum, but for citizens of Iran, China, Egypt or Libya, this is a far more important issue that can literally mean prison or death. On the order of 1/4 to 1/5 of the world cannot safely access social networks. So whether it be for functional, moral or political reasons, I think there is still a huge and interesting space of fully distributed, P2P applications that has yet to arise.&lt;/p&gt;

&lt;h3 id='p2p_social_networking'&gt;P2P social networking&lt;/h3&gt;

&lt;p&gt;Think about a P2P rather than web based social network. Facebook (or Google+, which I&amp;#8217;ve yet to get access to) would be even better if your whole social graph and all the data it entailed sat encrypted on your hard drive. Everything would be always available, low latency, and accessible in a way that lets users think of new ways to make use of &lt;em&gt;their data,&lt;/em&gt; rather than waiting around for a new feed or API to get at images, updates, preferences, birthdays, or whatever you care about. You could make friends off the grid by sharing keys with people you meet, and whenever you hop online your local data would be synced with other peers. Cloud based proxy peers could be used to improve availability, but the primary source of data would always be at your fingertips. On top of such a system user communities could decide to store all kinds of interesting information pertinent to their interests, and they could make use of it in the ways they choose. The Clojure community, for example, could store code snippets, git repositories, interesting papers, book reviews, and hammock designs in their local data stores. People could then write new &amp;#8220;apps&amp;#8221; by issuing graph queries against their peer&amp;#8217;s data, and creating new views on top of it. How does someone&amp;#8217;s reading list correspond to their code? How about popping up an ad-hoc chat room with everyone currently using clojure.contrib.probabilities.monte-carlo to ask for help or find some example code. This is where I think the future of P2P could and should go. It is far more resistant to failure, difficult to impossible for governments and companies to censor and control, and much more empowering and interesting for the user.&lt;/p&gt;

&lt;h3 id='distributed_plasma'&gt;Distributed Plasma&lt;/h3&gt;

&lt;p&gt;All that said, Plasma is a first experiment in trying to sew the seeds for such a platform. The idea is that each peer will store their data in a local graph database, and then they can choose what data to make available to the P2P network. Applications will access data on the network by issuing graph queries, which can transparently cross network boundaries to gather data in a declarative way, freeing the developer from the pain and suffering of network programming against a churning mass of peers. This is very much an experimental research project and there are already a number of things I can see where graph queries don&amp;#8217;t cut it, but I think it does provide a lot of desired functionality. This post is long enough for today though, so if you got this far tune in tomorrow for some actual distributed graph query examples.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Facebook Vibes</title>
     <link href="http://lifeisagraph.com/2011/07/04/facebook-vibes.html"/>
     <updated>2011-07-04T00:00:00+02:00</updated>
     <id>http://lifeisagraph.com/2011/07/04/facebook-vibes</id>
     <content type="html">&lt;p&gt;So today Facebook&amp;#8217;s video chat went live. I saw the &lt;a href='https://blog.facebook.com/blog.php?post=10150223135777131'&gt;headline&lt;/a&gt; on &lt;a href='http://news.ycombinator.com'&gt;Hacker News&lt;/a&gt; and minutes later my mom popped up on Facebook chat, so what better time to give it a whirl. I hit download to give it a try, and after grabbing a &lt;em&gt;jar&lt;/em&gt; file and running an install procedure we were up and running. The chat went pretty well, although it did hang and jitter a couple times. It got me wondering though, is this using my installed skype, or did it install a second copy of skype somewhere, or are they bundling the skype protocols in some kind of library? As any curious geek would, I started checking things out.&lt;/p&gt;

&lt;p&gt;The download link grabs a file called FacebookVideoCalling.jar that&amp;#8217;s only 27840 bytes in size, so it seemed highly unlikely that this was the entire codec. After scoping out the jar file a bit I found that it uses &lt;a href='http://jdk6.java.net/plugin2/liveconnect/'&gt;LiveConnect&lt;/a&gt; to let the java applet communicate with the javascript dom in the browser.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='java'&gt;  &lt;span class='kd'&gt;protected&lt;/span&gt; &lt;span class='n'&gt;JSObject&lt;/span&gt; &lt;span class='n'&gt;util&lt;/span&gt;&lt;span class='o'&gt;;&lt;/span&gt;
  &lt;span class='c1'&gt;// ...&lt;/span&gt;
  &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;window&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;JSObject&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;getWindow&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
  &lt;span class='c1'&gt;// ...&lt;/span&gt;
  &lt;span class='n'&gt;String&lt;/span&gt; &lt;span class='n'&gt;str1&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;getParameter&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;appid&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
  &lt;span class='n'&gt;String&lt;/span&gt; &lt;span class='n'&gt;str2&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;getParameter&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;userid&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This way they can get your Facebook user ID, and interestingly they also grab an application ID. With this information they formulate a signed request to download the application. The interesting thing to note is that this installer supports two applications:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='java'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;paramString&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;equals&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;com.facebook.peep&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;))&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;window&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;getMember&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;VideoChatPlugin&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='n'&gt;paramString&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;equals&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;com.facebook.vibes&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;))&lt;/span&gt; &lt;span class='o'&gt;{&lt;/span&gt;
      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;this&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;window&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='na'&gt;getMember&lt;/span&gt;&lt;span class='o'&gt;(&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;MusicDownloadDialog&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;);&lt;/span&gt;
    &lt;span class='o'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The video chat plugin, called peep, is what is downloaded now. At some point in the future they seem to be prepared to download another app though, called Facebook Vibes. I searched around to see what this is all about, and it seems that this is an unannounced feature that has yet to be released. The vibes app connects with a music download dialog in the page though, so I&amp;#8217;m guessing that with this release we are seeing the seeds for Facebook&amp;#8217;s upcoming music offering.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s too late to look into this further and I still haven&amp;#8217;t found the codecs, but anyways, you heard it here first: Facebook Vibes.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>The Plasma graph query engine</title>
     <link href="http://lifeisagraph.com/2011/06/12/plasma-a-graph-query-engine.html"/>
     <updated>2011-06-12T00:00:00+02:00</updated>
     <id>http://lifeisagraph.com/2011/06/12/plasma-a-graph-query-engine</id>
     <content type="html">&lt;p&gt;The &lt;a href='http://neo4j.org/'&gt;Neo4J&lt;/a&gt; team recently blogged about a graph query language called &lt;a href='http://blog.neo4j.org/2011/06/kiruna-stol-14-milestone-4.html'&gt;Cypher&lt;/a&gt;, and reading their post got me motivated to write about something I&amp;#8217;ve been working on for a while. &lt;a href='https://github.com/rosejn/plasma'&gt;Plasma&lt;/a&gt; is a graph query engine written in Clojure. Currently it sits on top of the &lt;a href='https://github.com/ninjudd/jiraph'&gt;Jiraph&lt;/a&gt; graph database that was written by &lt;a href='http://ninjudd.com/'&gt;Justin Balthrop&lt;/a&gt; and &lt;a href='https://github.com/lancepantz'&gt;Lance Bradley&lt;/a&gt; for a geneology website, &lt;a href='http://geni.com'&gt;Geni&lt;/a&gt;. (It would be less than a days work to get it running on top of another graph database though.) The query engine is built using a library of query operators that are combined to form dataflow graphs, and it uses &lt;a href='http://ideolalia.com/'&gt;Zach Tellman&amp;#8217;s&lt;/a&gt; asynchronous events library, &lt;a href='https://github.com/ztellman/lamina'&gt;Lamina&lt;/a&gt;, to provide concurrent, non-blocking execution of queries.&lt;/p&gt;

&lt;h3 id='query_language_examples'&gt;Query language examples&lt;/h3&gt;

&lt;p&gt;These query examples will be running against the test graph shown below (click for a big version). This graph represents the ACME company, which has three Swiss offices in Lugano, Geneva and Zurich. Each office has a manager, and the company produces a set of components which are combined to form widgets. (Have thoughts on more interesting example graphs? Please leave them in the comments because I&amp;#8217;d like some more.)&lt;/p&gt;

&lt;p&gt;&lt;a href='/media/acme-graph-big.png'&gt;&lt;img src='/media/acme-graph-small.png' alt='ACME example graph' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full code for these examples, including the graph construction, is available in the github project &lt;a href='https://github.com/rosejn/plasma/blob/master/test/example/query.clj'&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the examples below I&amp;#8217;ll be using the plasma.query namespace:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;require&lt;/span&gt; &lt;span class='o'&gt;&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;plasma&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;query&lt;/span&gt; &lt;span class='nv'&gt;:as&lt;/span&gt; &lt;span class='nv'&gt;query&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The functions in the query API operate on and produce a query plan, which can then be executed any number of times by passing it to the query function.&lt;/p&gt;

&lt;h3 id='select_nodes_with_path_expressions'&gt;Select nodes with path expressions&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='c1'&gt;; select all component nodes using a basic path expression&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;

&lt;span class='c1'&gt;; same query, using a regular expression for the component edge&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='s'&gt;&amp;quot;comp.*&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;

&lt;span class='c1'&gt;; same query, with bindings to the product and component nodes&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;product&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
             &lt;span class='nv'&gt;component&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A path expression starts at the root node of the graph by default, but you can start from another node by passing an argument to the query function when executed or by using the starting node&amp;#8217;s id as the first element of the path.&lt;/p&gt;

&lt;p&gt;The elements of a path expression are edge predicates. Keyword and regexp predicates will be matched against the :label property of an edge, and a map of predicates can be used to compare multiple edge properties.&lt;/p&gt;

&lt;p&gt;Executing the queries above will return a list of maps containing the ids of the component nodes.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:6df19085-0342-41a8-90b9-4d23f076e29d&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:16e2c756-2f44-4414-a007-417a19ba71a0&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:16e2c756-2f44-4414-a007-417a19ba71a0&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:ae990134-acfd-40f0-aef5-413be7b1002b&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:6729a33b-6cb7-44ad-921b-9a2ed0fe2bfc&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='project_node_properties'&gt;Project node properties&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;-&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;comp&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/project&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;comp&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:cost&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;24.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-1&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;8.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-2&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;8.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-2&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;75.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-3&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;120.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-4&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also project multiple nodes along the path, and optionally rename properties in case of conflicts, like the :label property here.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;-&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;p&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='nv'&gt;c&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;p&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/project&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;p&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:as&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt;&lt;span class='p'&gt;]]&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;c&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:cost&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;24.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-1,&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt; &lt;span class='nv'&gt;:alpha&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;8.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-2,&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt; &lt;span class='nv'&gt;:alpha&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;8.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-2,&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt; &lt;span class='nv'&gt;:beta&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;75.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-3,&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt; &lt;span class='nv'&gt;:beta&lt;/span&gt;&lt;span class='p'&gt;}&lt;/span&gt;
 &lt;span class='p'&gt;{&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;120.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-4,&lt;/span&gt; &lt;span class='nv'&gt;:product-label&lt;/span&gt; &lt;span class='nv'&gt;:beta&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='filter_using_the_where_clause'&gt;Filter using the where clause&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='c1'&gt;; Select the components that would cost more than $500 if using a 5x markup&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;-&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;component&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/where&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;* &lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;:cost&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;component&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='mi'&gt;500&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/project&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;component&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:cost&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:cost&lt;/span&gt; &lt;span class='mf'&gt;120.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:component-4&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that the where form supports arbitrary expressions involving node properties. All arithmetic functions, boolean logic, bit operations and many java.lang.Math functions (e.g. trig, log, etc..) are supported and its easy to add more.&lt;/p&gt;

&lt;p&gt;The where filter doesn&amp;#8217;t always have to apply to the last node of a path expression. Here is a query that will select products with components made in Geneva.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;product&lt;/span&gt;   &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
               &lt;span class='nv'&gt;component&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;product&lt;/span&gt; &lt;span class='nv'&gt;:component&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
               &lt;span class='nv'&gt;location&lt;/span&gt;  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;component&lt;/span&gt; &lt;span class='nv'&gt;:made-in&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/where&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;= &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;:label&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;location&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;:geneva-office&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/distinct*&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;product&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/project&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;product&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt;&lt;span class='p'&gt;]))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:beta&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='sort_with_orderby_limit_and_choose_results'&gt;Sort with order-by, limit, and choose results&lt;/h3&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='c1'&gt;; Get the most expensive product&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;-&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;product&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:product&lt;/span&gt;&lt;span class='p'&gt;]])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/order-by&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;product&lt;/span&gt; &lt;span class='nv'&gt;:price&lt;/span&gt; &lt;span class='nv'&gt;:desc&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/project&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;product&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:price&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/limit&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:price&lt;/span&gt; &lt;span class='mf'&gt;600.0&lt;/span&gt;&lt;span class='o'&gt;,&lt;/span&gt; &lt;span class='nv'&gt;:label&lt;/span&gt; &lt;span class='nv'&gt;:beta&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The choose operator can be used to select a random subset of the results.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='c1'&gt;; Get a random manager node&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;-&amp;gt; &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/path&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;:location&lt;/span&gt; &lt;span class='nv'&gt;:managed-by&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;query/choose&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;

&lt;span class='c1'&gt;; result:&lt;/span&gt;
&lt;span class='p'&gt;({&lt;/span&gt;&lt;span class='nv'&gt;:id&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;UUID:8f53c132-4f7e-4ef9-a192-2ff069d4ac61&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='and_more'&gt;and more&amp;#8230;&lt;/h3&gt;

&lt;p&gt;In addition you can compute aggregate values over specific properties using query/min, query/max, query/avg, and query/count*. There is also a basic graph construction API to make it easier to store sub-graphs programmatically. Checkout &lt;a href='https://github.com/rosejn/plasma/blob/master/test/example/query.clj'&gt;query.clj&lt;/a&gt; on github for examples.&lt;/p&gt;

&lt;h3 id='distributing_graph_queries'&gt;Distributing Graph Queries&lt;/h3&gt;

&lt;p&gt;The Plasma engine has also been designed to support distributed queries, where a path expression can cross from one graph to another on the local machine, or from one machine to another over the network. In a future post I&amp;#8217;ll show an example graph that contains proxy nodes, which act as bridges to remote graphs. Distributed path expressions can be used not only to query large graph databases spread across many servers, but they can also be the basis for a new kind of distributed system. I&amp;#8217;m not sure what the right role for such a distributed graph network is yet, but I think it has some interesting possibilities for supporting a peer-to-peer infrastructure.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Overtone demo video</title>
     <link href="http://lifeisagraph.com/2011/04/25/overtone-demo-video.html"/>
     <updated>2011-04-25T00:00:00+02:00</updated>
     <id>http://lifeisagraph.com/2011/04/25/overtone-demo-video</id>
     <content type="html">&lt;p&gt;&lt;a href='http://sam.aaron.name/'&gt;Sam&lt;/a&gt; put together a great demo video of live-coding with &lt;a href='http://github.come/overtone/overtone'&gt;Overtone&lt;/a&gt;. It shows both programming real-time synthesizers and also making music in about 4 minutes. (His pimped out emacs config is also getting a bit of attention&amp;#8230;)&lt;/p&gt;
&lt;iframe src='http://player.vimeo.com/video/22798433?title=0&amp;amp;byline=0&amp;amp;portrait=0' frameborder='0' height='300' width='400'&gt;
&lt;/iframe&gt;
&lt;p&gt;&lt;a href='http://vimeo.com/22798433'&gt;Quick Intro to Live Programming with Overtone&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more join the &lt;a href='http://news.ycombinator.com/item?id=2478794'&gt;hacker news discussion&lt;/a&gt; or checkout the &lt;a href='http://github.come/overtone/overtone'&gt;project on github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the future of music. Well, one future anyways :-)&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Debugging Clojure</title>
     <link href="http://lifeisagraph.com/2011/04/24/debugging-clojure.html"/>
     <updated>2011-04-24T00:00:00+02:00</updated>
     <id>http://lifeisagraph.com/2011/04/24/debugging-clojure</id>
     <content type="html">&lt;p&gt;I&amp;#8217;ve been debugging some clojure code lately so I thought I&amp;#8217;d gather some information here for others who might be doing the same. First, you should know about a couple tools that are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href='http://georgejahad.com/clojure/cdt.html'&gt;Clojure Debug Toolkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;which attaches to a running JVM, and then lets you set breakpoints and evaluate code in the scope of a suspended stack frame&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;&lt;a href='http://code.google.com/p/jswat/'&gt;JSwat Debugger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;a full fledged Java debugger that lets you set breakpoints in Java and/or Clojure, view bytecode, variables, stacks, etc.&lt;/p&gt;

&lt;p&gt;use with Clojure described &lt;a href='http://bc.tech.coop/blog/081023.html'&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I&amp;#8217;m happy these tools are available, they don&amp;#8217;t really jive with my development style so I&amp;#8217;ve been looking for other strategies. My day-to-day setup is inside of gvim using the &lt;a href='http://www.vim.org/scripts/script.php?script_id=2501'&gt;vimclojure&lt;/a&gt; plugin, and normally when I&amp;#8217;m debugging it is in the midst of developing some code. As I grow my program I&amp;#8217;m writing functions, interactively testing them on the repl, and occasionally adding &lt;a href='https://github.com/rosejn/logjam'&gt;logging&lt;/a&gt; when I need to follow more complicated program flow. So having to restart the JVM with debug options or connect externally with a separate program is too heavyweight. In fact, I&amp;#8217;m looking for lighter-weight debug strategies, not heavier weight.&lt;/p&gt;

&lt;h3 id='clojurecontribtrace'&gt;clojure.contrib.trace&lt;/h3&gt;

&lt;p&gt;One great tool I recently started using is clojure.contrib.trace. If you don&amp;#8217;t use it, I recommend taking it out for a spin. In short, it lets you follow the program flow by printing input arguments and return values as functions get called.&lt;/p&gt;

&lt;p&gt;Here, give it a try. Paste this implementation of the &lt;a href='http://en.wikipedia.org/wiki/Collatz_conjecture'&gt;Collatz conjecture&lt;/a&gt; into the repl. (Inspired by the &lt;a href='http://github.com/ztellman/aleph'&gt;Aleph&lt;/a&gt; pipeline docs&amp;#8230;) Collatz conjectured that if you take any number and then recursively divide by two when even or multiply by three and add one if it is odd, you will always arrive at 1. Seems pretty useless, although it is interesting to think about the sort of expansion and compression phases of the number as it makes its way to zero. I wonder if there are any natural processes that have a similar form? Anyway, we were tracing.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;use&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;clojure&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;contrib&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;trace&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;up&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;+ &lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;* &lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;down&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;collatz&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;cond&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;= &lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt; &lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;even?&lt;/span&gt; &lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;recur&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;down &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
    &lt;span class='nv'&gt;:else&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;recur&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;up &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now instead of inserting println statements to follow the program flow, trace it like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='nv'&gt;collatz&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;core=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;dotrace&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;up&lt;/span&gt; &lt;span class='nv'&gt;down&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;collatz&lt;/span&gt; &lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1012:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;down &lt;/span&gt;&lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1012:&lt;/span&gt; &lt;span class='nv'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1013:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;down &lt;/span&gt;&lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1013:&lt;/span&gt; &lt;span class='nv'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1014:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;down &lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nv'&gt;TRACE&lt;/span&gt; &lt;span class='nv'&gt;t1014:&lt;/span&gt; &lt;span class='nv'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='form_debugging_macro'&gt;Form debugging macro&lt;/h3&gt;

&lt;p&gt;Another helpful little tool I ran into somewhere is this debug macro:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defmacro &lt;/span&gt;&lt;span class='nv'&gt;dbg&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;x&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='o'&gt;`&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;x&lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt; &lt;span class='nv'&gt;~x&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;println &lt;/span&gt;&lt;span class='s'&gt;&amp;quot;dbg:&amp;quot;&lt;/span&gt; &lt;span class='ss'&gt;&amp;#39;~x&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;=&amp;quot;&lt;/span&gt; &lt;span class='nv'&gt;x&lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;x&lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It lets you easily wrap a form in your code, and then print the form and it&amp;#8217;s result whenever it gets evaluated. For example, redefine the down function like this:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;down&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;dbg&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And now when run on the repl it will produce:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='nv'&gt;collatz&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;core=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;collatz&lt;/span&gt; &lt;span class='mi'&gt;8&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='nv'&gt;dbg:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;span class='nv'&gt;dbg:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;
&lt;span class='nv'&gt;dbg:&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;/ &lt;/span&gt;&lt;span class='nv'&gt;v&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;
&lt;span class='mi'&gt;1&lt;/span&gt;
&lt;span class='nv'&gt;collatz&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;core=&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 id='tracking_down_wild_threads'&gt;Tracking down wild threads&lt;/h3&gt;

&lt;p&gt;For one really frustrating bug I had recently in the &lt;a href=''&gt;Plasma&lt;/a&gt; query engine, I had a thread that was running wild and I couldn&amp;#8217;t figure out either what thread it was or where it was hanging up. For tracking down threads I&amp;#8217;ve got a couple tools for you. First, I found this &lt;a href='http://lsd.luminis.nl/top-threads-plugin-for-jconsole/'&gt;top threads&lt;/a&gt; jconsole plugin very helpful. It lets you see all the running threads sorted by CPU usage like unix top, and if you select a thread you can view the current stackframe.&lt;/p&gt;

&lt;p&gt;While that is definitely a nice tool, again I wanted something quick and easy to run on the repl. After looking into the JVM APIs used to get at this info, I put together a couple functions for my user.clj:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;import &lt;/span&gt;&lt;span class='ss'&gt;&amp;#39;java&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;lang&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;management&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;ManagementFactory&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;threads&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Get a seq of the current threads.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;grp&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getThreadGroup&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;Thread/currentThread&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
        &lt;span class='nv'&gt;cnt&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;activeCount&lt;/span&gt; &lt;span class='nv'&gt;grp&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nv'&gt;ary&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;make-array &lt;/span&gt;&lt;span class='nv'&gt;Thread&lt;/span&gt; &lt;span class='nv'&gt;cnt&lt;/span&gt;&lt;span class='p'&gt;)]&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;enumerate&lt;/span&gt; &lt;span class='nv'&gt;grp&lt;/span&gt; &lt;span class='nv'&gt;ary&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;seq &lt;/span&gt;&lt;span class='nv'&gt;ary&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;thread-grep&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Find a thread by name.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;name&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;ptn&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;re-pattern &lt;/span&gt;&lt;span class='nv'&gt;name&lt;/span&gt;&lt;span class='p'&gt;)]&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;filter &lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;re-find&lt;/span&gt; &lt;span class='nv'&gt;ptn&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getName&lt;/span&gt; &lt;span class='nv'&gt;%&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;threads&lt;/span&gt;&lt;span class='p'&gt;))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;get-thread&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Lookup a thread by its numeric ID.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;id&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;first &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;filter &lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;=&lt;/span&gt; &lt;span class='nv'&gt;id&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getId&lt;/span&gt; &lt;span class='nv'&gt;%&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;threads&lt;/span&gt;&lt;span class='p'&gt;))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;thread-top&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Return a seq of threads sorted by their total userland CPU usage.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;mgr&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;ManagementFactory/getThreadMXBean&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
        &lt;span class='nv'&gt;cpu-times&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;map &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;fn &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
                         &lt;span class='p'&gt;[(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getThreadCpuTime&lt;/span&gt; &lt;span class='nv'&gt;mgr&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getId&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
                    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;threads&lt;/span&gt;&lt;span class='p'&gt;))]&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;map&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;fn &lt;/span&gt;&lt;span class='p'&gt;[[&lt;/span&gt;&lt;span class='nv'&gt;cpu&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;]]&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;cpu&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getName&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getId&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='nv'&gt;t&lt;/span&gt;&lt;span class='p'&gt;])&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;reverse &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;sort-by &lt;/span&gt;&lt;span class='nv'&gt;first&lt;/span&gt; &lt;span class='nv'&gt;cpu-times&lt;/span&gt;&lt;span class='p'&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This way you can quickly see which thread is pegging the cpu, or lookup a thread if you&amp;#8217;ve assigned it a name. I haven&amp;#8217;t added any helpers yet, but if you inspect the thread object with clojure.contrib.repl-utils/show you&amp;#8217;ll see the methods you can use to view the current stacktrace or (danger!) stop the thread. Might be nice to get some tools like this into contrib.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Overtone gets an event system</title>
     <link href="http://lifeisagraph.com/2010/03/15/clojure-event-system.html"/>
     <updated>2010-03-15T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2010/03/15/clojure-event-system</id>
     <content type="html">&lt;p&gt;Overtone now has a global event system. You can subscribe to any event:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;on&lt;/span&gt; &lt;span class='nv'&gt;:chorus-finished&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;fade-chorus-echo&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And publish any event:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;event&lt;/span&gt; &lt;span class='nv'&gt;::chorus-finished&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;optionally with added properties:&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;event&lt;/span&gt; &lt;span class='nv'&gt;::chorus-finished&lt;/span&gt; &lt;span class='nv'&gt;:timestamp&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;+ &lt;/span&gt;&lt;span class='nv'&gt;clock&lt;/span&gt; &lt;span class='nv'&gt;tick&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is a great example of the power of having a Lisp like Clojure on top of the JVM. Using a fixed thread pool executor from the java.util.concurrent package made this incredibly simple, and since we use a reference variable to store the event handlers all modifications are safe-guarded by the &lt;a href='http://en.wikipedia.org/wiki/Software_transactional_memory'&gt;STM&lt;/a&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;ns&lt;/span&gt; &lt;span class='nv'&gt;overtone&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;core&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;event&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;:import&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;java&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;util&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;concurrent&lt;/span&gt; &lt;span class='nv'&gt;Executors&lt;/span&gt; &lt;span class='nv'&gt;LinkedBlockingQueue&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;:use&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;overtone&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;core&lt;/span&gt; &lt;span class='nv'&gt;util&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;def &lt;/span&gt;&lt;span class='nv'&gt;NUM-THREADS&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;cpu-count&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;defonce&lt;/span&gt; &lt;span class='nv'&gt;thread-pool&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;Executors/newFixedThreadPool&lt;/span&gt; &lt;span class='nv'&gt;NUM-THREADS&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;defonce&lt;/span&gt; &lt;span class='nv'&gt;event-handlers*&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;ref &lt;/span&gt;&lt;span class='p'&gt;{}))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;on&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Runs handler whenever events of type event-type are fired.  The handler can &lt;/span&gt;
&lt;span class='s'&gt;  optionally except a single event argument, which is a map containing the &lt;/span&gt;
&lt;span class='s'&gt;  :event-type property and any other properties specified when it was fired.&lt;/span&gt;
&lt;span class='s'&gt;  &lt;/span&gt;
&lt;span class='s'&gt;  (on ::booted #(do-stuff))&lt;/span&gt;
&lt;span class='s'&gt;  (on ::midi-note-down (fn [event] (funky-bass (:note event))))&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='nv'&gt;handler&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;dosync&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;handlers&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;@event-handlers*&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='p'&gt;[])]&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;alter &lt;/span&gt;&lt;span class='nv'&gt;event-handlers*&lt;/span&gt; &lt;span class='nv'&gt;assoc&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;conj &lt;/span&gt;&lt;span class='nv'&gt;handlers&lt;/span&gt; &lt;span class='nv'&gt;handler&lt;/span&gt;&lt;span class='p'&gt;)))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;remove-handler&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Remove an event handler previously registered to handle events of&lt;/span&gt;
&lt;span class='s'&gt;event-type.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='nv'&gt;handler&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;dosync&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;handlers&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;@event-handlers*&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='p'&gt;[])]&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;alter &lt;/span&gt;&lt;span class='nv'&gt;event-handlers*&lt;/span&gt; &lt;span class='nv'&gt;assoc&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;filter &lt;/span&gt;&lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;not&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;= &lt;/span&gt;&lt;span class='nv'&gt;handler&lt;/span&gt; &lt;span class='nv'&gt;%&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;span class='nv'&gt;handlers&lt;/span&gt;&lt;span class='p'&gt;)))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;clear-handlers&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Remove all handlers for events of type event-type.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;event-type&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;dosync &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;alter &lt;/span&gt;&lt;span class='nv'&gt;event-handlers*&lt;/span&gt; &lt;span class='nv'&gt;dissoc&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn- &lt;/span&gt;&lt;span class='nv'&gt;handle-event&lt;/span&gt; &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;event&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;doseq &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;handler&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;get &lt;/span&gt;&lt;span class='nv'&gt;@event-handlers*&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;:event-type&lt;/span&gt; &lt;span class='nv'&gt;event&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;[])]&lt;/span&gt;
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;if &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;zero? &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;arg-count&lt;/span&gt; &lt;span class='nv'&gt;handler&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;handler&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;handler&lt;/span&gt; &lt;span class='nv'&gt;event&lt;/span&gt;&lt;span class='p'&gt;))))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;event&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Fire an event of type event-type with any number of additional properties.&lt;/span&gt;

&lt;span class='s'&gt;  (event ::my-event)&lt;/span&gt;
&lt;span class='s'&gt;  (event ::filter-sweep-done :instrument :phat-bass)&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;event-type&lt;/span&gt; &lt;span class='nv'&gt;&amp;amp;&lt;/span&gt; &lt;span class='nv'&gt;args&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;execute&lt;/span&gt; &lt;span class='nv'&gt;thread-pool&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;handle-event&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;apply &lt;/span&gt;&lt;span class='nv'&gt;hash-map&lt;/span&gt; &lt;span class='nv'&gt;:event-type&lt;/span&gt; &lt;span class='nv'&gt;event-type&lt;/span&gt;
&lt;span class='nv'&gt;args&lt;/span&gt;&lt;span class='p'&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The only external code besides this file were a couple functions I added to the utilities for getting the number of available CPU cores and getting the arity of a function.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;cpu-count&lt;/span&gt;
  &lt;span class='s'&gt;&amp;quot;Get the number of CPUs on this machine.&amp;quot;&lt;/span&gt;
  &lt;span class='p'&gt;[]&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;availableProcessors&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;Runtime/getRuntime&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;

&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;defn &lt;/span&gt;&lt;span class='nv'&gt;arg-count&lt;/span&gt; 
  &lt;span class='s'&gt;&amp;quot;Get the arity of a function.&amp;quot;&lt;/span&gt; 
  &lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;f&lt;/span&gt;&lt;span class='p'&gt;]&lt;/span&gt; 
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='k'&gt;let &lt;/span&gt;&lt;span class='p'&gt;[&lt;/span&gt;&lt;span class='nv'&gt;m&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;first &lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getDeclaredMethods&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;class &lt;/span&gt;&lt;span class='nv'&gt;f&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt; 
        &lt;span class='nv'&gt;p&lt;/span&gt; &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nv'&gt;getParameterTypes&lt;/span&gt; &lt;span class='nv'&gt;m&lt;/span&gt;&lt;span class='p'&gt;)]&lt;/span&gt; 
    &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;alength &lt;/span&gt;&lt;span class='nv'&gt;p&lt;/span&gt;&lt;span class='p'&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is probably not optimal in a couple areas, but it should get us pretty far. I added the arg-count detection because in my very first test of the code I got an error because I was trying to use an event handler that took no arguments.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='clojure'&gt;  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;on&lt;/span&gt; &lt;span class='nv'&gt;::foo&lt;/span&gt; &lt;span class='o'&gt;#&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nv'&gt;println&lt;/span&gt; &lt;span class='s'&gt;&amp;quot;This is a foo event handler.&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
  &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nf'&gt;event&lt;/span&gt; &lt;span class='nv'&gt;::foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;It&amp;#8217;s so natural in clojure though, that I had to make it work. Now it will only pass the event argument if it sees that the arity isn&amp;#8217;t zero, so these kinds of event handlers will work fine. Performing this reflection on every event handler might be a bit of useless overhead, but until we get to that level of optimization I&amp;#8217;m not going to worry about it.&lt;/p&gt;

&lt;p&gt;The default right now is to use a thread pool with as many threads as there are available CPU cores. This is just a guess as to what will actually provide efficient handling of events though, so if we start using the event system for high-throughput events we may want to do some experiments.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Internet Exchange</title>
     <link href="http://lifeisagraph.com/2010/03/06/internet-exchange.html"/>
     <updated>2010-03-06T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2010/03/06/internet-exchange</id>
     <content type="html">&lt;p&gt;This is a well done video about the decentralized architecture of the internet and exchange points. It&amp;#8217;s kind of a video info-graphic.&lt;/p&gt;
&lt;object height='385' width='640'&gt;&lt;param name='movie' value='http://www.youtube.com/v/a5837LcDHfE&amp;hl=en_US&amp;fs=1&amp;' /&gt;&lt;param name='allowFullScreen' value='true' /&gt;&lt;param name='allowscriptaccess' value='always' /&gt;&lt;embed src='http://www.youtube.com/v/a5837LcDHfE&amp;hl=en_US&amp;fs=1&amp;' allowfullscreen='true' type='application/x-shockwave-flash' allowscriptaccess='always' height='385' width='640' /&gt;&lt;/object&gt;
&lt;p&gt;I remember dreaming of animations like this while studying networking, algorithms and data structures. I hope one day there are graphics and animation libraries made to visualize program structures like this, so you could just insert some event sends to update the state of the visualization and it would take care of the rest. (i.e. pretty graphics, interpolation, etc&amp;#8230;) There are so many chart and graph libraries out there, but it seems like the world of info-graphics could be so much richer.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>A New Os Abstraction</title>
     <link href="http://lifeisagraph.com/2010/02/28/a-new-os-abstraction.html"/>
     <updated>2010-02-28T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2010/02/28/a-new-os-abstraction</id>
     <content type="html">&lt;p&gt;Is there a better metaphor than the Unix file interface for a 21st century OS, or are OS level services even the right level to be unifying behind a common abstraction? Read here first&amp;#8230;&lt;/p&gt;

&lt;p&gt;http://bywicket.com/users/mikel/weblog/fbc2a/Closos.html&lt;/p&gt;

&lt;p&gt;Using objects, closures, prototypes, frames or whatever you want to call chunks of state and code, seems like the wrong approach to me. Unix files are really encompass two different types of abstractions: a hierarchical namespace interface using directories, and a byte stream oriented interface for doing I/O. Using something as generic as a closure or an object seems like going against the benefits of finding a unified interface.&lt;/p&gt;

&lt;p&gt;I think the next generation metaphor will be some kind of database layer with a standardized data model and a powerful query system. The utility of google style full-text search, spotlight and quicksilver for intelligent access to data and applications, and the rich libraries modern programming languages have for interacting with sequences and databases all point to a DB abstraction. Limiting the functional capabilities to a query language also makes this type of abstraction suitable for distributed data access layers, where you wouldn&amp;#8217;t want to be executing foreign code.Edit2:33 pm&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Coming Alive</title>
     <link href="http://lifeisagraph.com/2010/02/23/coming-alive.html"/>
     <updated>2010-02-23T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2010/02/23/coming-alive</id>
     <content type="html">&lt;p&gt;Some good reading today:&lt;/p&gt;

&lt;p&gt;On John Koza&amp;#8217;s software evolution system that has successfully evolved multiple inventions that have been awarded patents. This could become a very interesting area as computing power and cheap access to the cloud keeps ramping up.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.popsci.com/scitech/article/2006-04/john-koza-has-built-invention-machine'&gt;John Koza&amp;#8217;s Invention Machine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the present and future of games that blend reality and the virtual world. Rode your bike to work today instead of driving, 200 points! Cooked your own dinner&amp;#8230; boooo-yah, 500 points. You just saved on your health insurance payment for the day.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://fury.com/2010/02/jesse-shells-mindblowing-talk-on-the-future-of-games-dice-2010/'&gt;The Future of Games&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On Emmy Miller, a software composer (as in a program that learns and autonomously generates pieces of music) that is coming out with an album shortly.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.miller-mccune.com/culture-society/triumph-of-the-cyborg-composer-8507/'&gt;Cyborg Composer&lt;/a&gt;&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Blog Engines Are Overrated</title>
     <link href="http://lifeisagraph.com/2009/12/14/blog-engines-are-overrated.html"/>
     <updated>2009-12-14T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2009/12/14/blog-engines-are-overrated</id>
     <content type="html">&lt;p&gt;Well after spending far too long experimenting with a variety of blog engines and other annoying &amp;#8220;web applications&amp;#8221;, I&amp;#8217;ve realized that really I want to be able to blog using the same tools that I use to develop software. Why waste so much time and effort writing posts in an HTML form window when you sit inside of a text editor most of your life anyway? So, here we go again. Now using Jekyll, the git-hub style, blogging-plus, toolchain.&lt;/p&gt;</content>
   </entry>
 
   <entry>
     <title>Livecoding in Clojure</title>
     <link href="http://lifeisagraph.com/2009/11/11/livecoding-clojure.html"/>
     <updated>2009-11-11T00:00:00+01:00</updated>
     <id>http://lifeisagraph.com/2009/11/11/livecoding-clojure</id>
     <content type="html">&lt;p&gt;
This is just a random blog post full of garbage so I can see how it looks inside
this new fangled template.  Jekyll, less for CSS and git for the core seems to
be a pretty sweet way of doing things...
&lt;/p&gt;
&lt;pre&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/pre&gt;

&lt;pre&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bar&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;+ &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dosync &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;alter &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;asdf*&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;assoc&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;:asd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/pre&gt;

&lt;h3&gt;Variable sizes&lt;/h3&gt;

&lt;p&gt;
  The initial version supported changing the ON/OFF label text, but if you made
the text longer than a few characters, it would be hidden under the handle. I
set about making the handle and label sizes automatic based on the actual
label text.
&lt;/p&gt;
</content>
   </entry>
 

</feed>
