Monday, April 26, 2010

let and binding macros in Clojure

Clojure has a special form called "let" and "binding", which are actually implemented as macros. It can get confusing sometimes.  If you have read "Programming Clojure" book, you might be thinking that it is easy to use them correctly considering following straightforward example:
Basically, "let" creates a lexical scoped Vars, which is local to the code block or a procedure where it is initially defined. Therefore, in the example above, "let" just created a new "x" with the value of "new value". But it is not local to the function "print-x".  That is to say, "print-x" have no knowledge of the local scoped binding that is created by "let". "binding" macro instead creates a new binding for the already existed var, "x" with the "new value". Since "print-x" is evaluated within the "binding" form, the newly bound value is visible to any chained calls within the form.  All nice and easy. However, it is not that obvious to understand and use them correctly. Look at following example:
"let" example is just fine. It created a new var "y" with the value of 5 bound to it.  But what is going on with the "binding" example? If we dig little deeper to Clojure API doc, we learn that the new bindings in "binding" are made in parallel, not sequential as it is in "let". Therefore, existing var "y" gets bound to the root binding of "x" not the overshadowed value of "x", which is the case in the "let example".
OK, so far so good. Let us look at another example:
Line 20-29 is pretty straight forward. But how about line 30? Shouldn't  it call "print-y" function? Not really. It is because the anonymous function is evaluated outside of the "binding" form.





To see this in action let us try following:



The "#<user$eval__28$fn__30 user$eval__28$fn__30@bdb503>" is an indication that anonymous function did not get evaluated within the "binding" form.  Ok, now, how do you make sure it evaluates within the "binding" form? Here is what you do:



Just enclose the anonymous function within parenthesis.  What do we learn from all of the above? Here are the summary that helps one use "let" and "binding" correctly:
  • Use "binding" only if you are trying to alter a behavior dynamically for special-vars that is already defined. Enclose your special vars within * as recommended by idiomatic clojure to make your intent clear.
  • Use "let" for locally scoped vars and avoid shadowing already defined vars.
  • Remember "let" binding is sequential and "binding" binding is done in parallel.
  • Pay close attention to the scope of the "binding" form. Any expression that is not evaluated inside the form, will not see the binding. That is to say, binding in "binding" form is thread local starting from the "binding" form until it ends including any expression that is chained within the form, which is what the thread-local are all about.
I posted this so that it helps someone trying to find his or her way in the wonderful world of Clojure.  

1 comment:

  1. Great idea! Love seeing a creative mind work and gain success!!!!!! Hope it continues to

    grow!

    regards.
    http://www.mayfairstationers.co.uk

    ReplyDelete