Clojure

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.
 

Reference: Got my feet wet with Clojure from our JCG partner David Pollak at the DPP’s Blog blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button