This post originated from an RSS feed registered with Ruby Buzz
by Jay Fields.
Original Post: Clojure: Destructuring
Feed Title: Jay Fields Thoughts
Feed URL: http://feeds.feedburner.com/jayfields/mjKQ
Feed Description: Blog about Ruby, Agile, Testing and other topics related to software development.
In The Joy of Clojure (TJoC) destructuring is described as a mini-language within Clojure. It's not essential to learn this mini-language; however, as the authors of TJoC point out, destructuring facilitates concise, elegant code.
What is destructuring?
Clojure supports abstract structural binding, often called destructuring, in let binding lists, fn parameter lists, and any macro that expands into a let or fn. -- http://clojure.org/special_forms
The simplest example of destructuring is assigning the values of a vector.
note: I'm using let for my examples of destructuring; however, in practice I tend to use destructuring in function parameter lists at least as often, if not more often.
I'll admit that I can't remember ever using destructuring like the first example, but it's a good starting point. A more realistic example is splitting a vector into a head and a tail. When defining a function with an arglist** you use an ampersand. The same is true in destructuring.
As the example shows, the values of :x and :y are bound to locals with the names the-x and the-y. In practice we would never prepend "the-" to our local names; however, using different names provides a bit of clarity for our first example. In production code you would be much more likely to want locals with the same name as the key. This works perfectly well, as the next example shows.
user=> (def point {:x 5 :y 7}) #'user/point
user=> (let [{x :x y :y} point] (println "x:" x "y:" y)) x: 5 y: 7
While this works perfectly well, creating locals with the same name as the keys becomes tedious and annoying (especially when your keys are longer than one letter). Clojure anticipates this frustration and provides :keys directive that allows you to specify keys that you would like as locals with the same name.
There are a few directives that work while destructuring maps. The above example shows the use of :keys. In practice I end up using :keys the most; however, I've also used the :as directive while working with maps.
The following example illustrates the use of an :as directive to bind a local with the entire map.
We've now seen the :as directive used for both vectors and maps. In both cases the local is always assigned to the entire expression that is being destructured.
For completeness I'll document the :or directive; however, I must admit that I've never used it in practice. The :or directive is used to assign default values when the map being destructured doesn't contain a specified key.
user=> (def point {:y 7}) #'user/point
user=> (let [{:keys [x y] :or {x 0 y 0}} point] (println "x:" x "y:" y)) x: 0 y: 7
Lastly, it's also worth noting that you can destructure nested maps, vectors and a combination of both.