Foognostic blogs Seeking knowledge of foo

8Feb/10Off

A swank growl

I ran into a frustrating puzzle while learning about agents in Clojure. The problem was with a tool, not Clojure itself but a few neat things shook out along the way.

Agents in Clojure are a high level concept for concurrency. They are intended for independent, asynchronous data munging. They're pretty easy to use... basically a value is associated with an agent when it is created, and then when a function is applied to the agent the result becomes its new value.

Here is the definition of a banana agent:

(def banana (agent "ba"))

Here is the function which produces its new value:

(defn tally-man [berry]
  (let [input berry, output (str berry "na")]
    (println (format "input:output %s:%s" input output))
    output))

Here it is in action:

user> (def banana (agent "ba"))
(await (send banana tally-man))
@banana

"bana"
user> (await (send banana tally-man))
@banana

"banana"

The debug statements are missing! Code invoked by (send) doesn't yield output to the REPL -- it went to a different buffer.

The major symptom of my pernicious problem was that my precious debug statements weren't printing anything. This was a major problem since that was the only way I had to know what was happening. At this point it's important to disclose that I am an unrepentant GNU/Emacs user. This leads to using swank-clojure since a symbiotic REPL is really, really cool.

After much confusion I finally found my precious debug statements! They were hiding in the rarely-used inferior-lisp buffer. It's awkward for me to keep this buffer open so I looked for something... cooler.

Growl is a MacOS X utility which displays notifications in self-dismissing windows. The gap between Clojure and Growl is little more than implementing java.io.Writer. Clojure actually makes it easier than it should be.

(defn growl [msg]
  (.exec (Runtime/getRuntime)
         (str "/path/to/growlnotify -a emacs -m " msg)))

(def growler
  (proxy
    [java.io.Writer] []
    (flush [] )
    (write [text] (growl text))))

It's worth taking a close look at the proxy statement, specifically the write method. It is an abstract, overloaded method in Writer. Only one of those overloads is abstract, and it's NOT the one implemented above. The one implemented above is the only one actually called. Or, you know, quacked. That's neat! I didn't even have to implement all of the abstract methods! All I had to do is what I actually needed to do.

The last piece of the puzzle was making Clojure use the Growl bridge. The binding function made this happen with one line of code. out is the "variable" which Clojure uses for writing to standard out. A call to (binding) gives out a new value. and the old value is restored when (binding) exits. All of the compiled references to out within Clojure automatically use my binding. TOO COOL.

(defn tally-man [berry]
  (let [input berry, output (str berry "na")]
    (binding [*out* growler]
      (println (format "input:output %s:%s" input output)))
      output))

Ta DA!

Filed under: clojure, code No Comments
   

Page optimized by WP Minify WordPress Plugin

Foognostic blogs is Stephen Fry proof thanks to caching by WP Super Cache