Got my feet wet with Clojure
I started hardcode coding on Project Plugh. I’m working on moving a bunch of Lift concepts over to Clojure as I build Lift’s comet facilities in Clojure so I can stream data to the browser.
Background… PartialFunction
In Scala, there’s a PartialFunction
The key take-away for PartialFunctions is “… is a unary function where the domain does not necessarily include all values of type A.”
The ability to test a PartialFunction
to see if the domain includes a particular value is very helpful. pf.isDefinedAt(x)
allows testing to see if the function is defined at a given value of x
.
But a PartialFunction
is a subclass of Function
, so PartialFunctions
can be applied:
pf(x)
The Scala compiler will take a pattern and turn it into a PartialFunction
:
def pf: PartialFunction[String, Number] = { case "" => 0 // special case blank to zero case x if isInt(x) => x.toInt case x if isDouble(x) => x.toDouble case x if isBigInt(x) => asBigInt(x) }
Another property of PartialFunction
is they can be composed:
pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place // any of the partial functions are defined
We use PartialFunctions
extensively in Lift to allow choosing if a particular URL should be served by Lift, if it should be served by a particular REST handler, etc. For example, defining a REST route in Lift:
serve { case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => User.find(userId).map(_.toJson) }
As I’ve been learning Clojure in preparation for a presentation at Strange Loop and as part of a new project I’ve been working on, I am looking to bring the best things in Lift into the Clojure code I write.
Into Clojure
Clojure’s pattern matching stuff is pretty nifty. I especially like how you can extract values out of a Map (this is so much more powerful that Scala’s pattern matching, even with unapply… but I digress).
So, I wrote a macro:
(defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body)))
This creates a function that is the application of the match to a parameter, so:
((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33) ;; 231
Turns out the Clojure pattern matcher will extract values into unbound variables. But bound variables are tested… this means that:
((match-func [[x y]] (+ x y)) [4 5])
Turns out this is a problem because x
is bound in the match-func
macro… so we need to change x
to something else. So, we have to change the variable x
to something else:
(defmacro match-func [& body] "Create a function that does pattern matching." `(fn [x#] (match [x#] ~@body)))
isDefinedAt
So, how do we test to see if the pattern matches at a particular value?
This became a challenge for me to wrap my brain around how things are done in Clojure. How do I have a function that represents the pattern match and be able to query it to see if it’s defined at a point without invoking the computation on the right-side which can be side-effecting.
The answer is arity. Scala has functions with defined arity. Turns out that Clojure can have a single function that behaves differently depending on the arity of the invocation. Yay!
So, the macro looks like:
(defmacro match-pfunc [& body] "Create a partial function that does pattern matching." (let [rewrite (mapcat (fn [x] [(first x) true]) (partition 2 body))] `(fn ([x#] (match [x#] ~@body)) ([x# y#] (cond (= :defined? x#) (match [y#] ~@rewrite) (= :body x#) '(~@body))))))
What this gives us is a function than can be invoked with a single parameter:
(pf 44)
And it can be invoked with 2 parameters:
(pf :defined? 44)
And I added the ability to get the body of the original so that I can add an orElse
function that will actually build a new PartialFunction
that is the compilation of the composed patterns so that the patterns will be compiled more efficiently.
First toe in the water
Yep. I think Clojure is pretty powerful. With macros, I’ve added one of the most amazingly powerful language feature of Scala to Clojure in a few lines. The water feels pretty good so far.