Thursday, June 10, 2010

On learning idiomatic Clojure

If you hang around in Clojure google groups and Clojure user groups, one topic that keep popping up is this: How one go about learning idiomatic way of writing Clojure code? After reading almost every source that I could get my hands on, I finally settled for this:
  1. Study Clojure.core daily
  2. Read "Joy of Clojure" The Joy of Clojure
  3. Code, Code, and Code in Clojure as much as you can
If you are really serious about Clojure, grab a MEAP edition of "Joy of Clojure" and the book bundle. Here is how I follow above steps: I find interesting functions from Clojure.core and will implement it by adding my- in front of standard functions. Then I search for the forms used within the function in the "Joy of Clojure".  It is quite an enlightening experience to see how the creator of the language structure his code. On top of that, you have an idiomatic Clojure reference book's help within your reach. Boy, it feels like a happy kid with a cotton candy in your hand.  Let me show you by an example:

 (def
 my-assoc
 (fn my-assoc
   ([map key val] (assoc map key val))
   ([map key val & kvs]
    (let [ret (my-assoc map key val)]
      (if kvs
        (recur ret (first kvs) (second kvs) (nnext kvs))
        ret))))) 
(println (my-assoc {} :symbol "assoc")) => {:symbo assoc}

my-assoc here is a Var that holds a reference to a function that named my-assoc, which takes a map and one or more key-val pairs, and returns a map that contains the mapping.  This actually is a classic Clojure style of tail recursive function. Namely, it is an idiomatic way to use function signatures for base and recursive cases. It is also very elegant to use let binding to hold accumulative value that will be referred in multiple places in a block. The use of recur in the tail position is just what it is intended for. It recursively invokes my-assoc function with the correct number of arguments, which in this case
(my-assoc [map key val & kvs])  until kvs are nil, which is one of only two false (nil and false) in idiomatic Clojure.  (first kvs) (second kvs) (nnext kvs) is also idiomatic way of traversing a seq that will save you from unexpected nil punning. (Again, read "Joy of Clojure" for detailed explanation for this). 

My way of learning idiomatic Clojure is very opinionated and might not work for everyone. What is your way of learning idiomatic Clojure? Please share it if you can so a Clojure noob will have a good start.

2 comments:

  1. Beware that core.clj is not always the most idiomatic: up to the redefinition of let to use destructuring most of the language is missing, then there is some legacy style (eg old interop with plain dot instead of .method and Foo/bar).

    ReplyDelete
  2. Thanks for pointing this out. I will keep this in mind.

    ReplyDelete