Wednesday, July 6, 2011

A strange behavior in Clojure

I see this behavior in Clojure 1.2.1:

user=> (type '(1 2))
user=> (type `(1 2)) ;; notice syntax-quote
user=> (type '(1))
user=> (type `(1))
I expected `(1) to be a Cons just like `(1 2) is.

I also tried:

user=> (type (cons 1 nil)) 
user=> (type (cons 1 `()))
user=> (type (cons 1 '()))
user=> (type (cons 1 []))

So what is the reason for `(1) and (cons 1 nil) to be PersistentLists?

It turns out that you shouldn't program against those exact types but against the seq abstraction.

However, I think I can take a guess at the reason. The Clojure forms that produce a PersistentList ultimately call, specifically the cons(Object x, Object coll) method. It begins with a pretty odd check: if(coll == null) return new PersistentList(x), after which it creates a Cons object if that check doesn't pass. If you look at earlier versions of the code, you can find this:

static public IPersistentCollection cons(Object x, IPersistentCollection y) {
    if(y == null)
        return new PersistentList(x);
    return y.cons(x);

So in an earlier version of the function, the call was dispatched to the cons method of the second argument, so the case when the second argument was null (i.e. nil in Clojure) needed special handling. Later versions don't do that dispatching (or actually do it but in a different way, presumably to support a larger variety of collection types), but the check has been retained since it does not break any correctly written code.