Object Commando languages, development and design


Narrowing the Scope of Globals with Let

I've been reading OnLisp by Paul Graham. It's about becoming a better Lisp programmer. It's written for Common Lisp, but I have found quite a bit of it carries over into Clojure. One interesting code snippit I found in the book was on using let when two functions required the same value. Previously I would have done this through a def, like below:

(def step-by 7)
(defn increment [x] (+ x step-by))
(defn decrement [x] (- x step-by))

Paul Graham instead approaches it like:

 (let [step-by 7]
	   (defn increment1 [x] (+ x step-by))
	   (defn decrement1 [x] (- x step-by)))

Obviously the example above is trivial, however there are times that shared immutable data is necessary. Database connection properties, connection URI information etc. I often have a small number of functions that need that sort of data. For these kinds of situations, I like this second approach. It more narrowly scopes the variable and I can't think of any drawbacks. I think this also highlights a difference in approach from imperative languages and functional languages in general. Paul (on page 30 of OnLisp) describes imperative language code structure as more "solid and blockish" whereas functional code tends to be more "fluid". The first example above fits the blockish imperative model where you define your variables and functions at the same level. This is exactly how I would go about it in Java. I've noticed that with the Clojure code I've been writing, and thinking back to code I've written in OCaml, it is definitely more fluid, a less rigid block structure. I've gone through about 6 chapters of the book thus far and am looking forward to reading through the chapters on macros.

Comments (1) Trackbacks (0)
  1. This approach is definitely useful, however it does have at least one significant drawback, which is that code constructed in this manner becomes somewhat inscrutible at the REPL.

    For simple examples like this, the problem is relatively minor, but let bound closures are opaque to interactive interrogation and development at the REPL. When the code complexity increases it’s usually beneficial to break these closures out to the top level.

    I used to write a lot of code like this, using let-fn bound local functions and closures, until I realised that it was significantly harder to develop with.

    Namespaces when used properly support private function and variable definitions, which prohibit access from other namespaces, but allow evaluation at the REPL, when in the same namespace. This allows you to hide information appropriately whilst supporting interrogation and debugging; making refactoring and re-structuring code at the REPL significantly easier.

Leave a comment


No trackbacks yet.