Zen and the art of...

2009-12-02

Overlooking the Obvious

Monday, Tim Bray posted another article on Clojure in his Concur.next series. In there is some beautiful code (as idiomatic Clojure usually is) written by John Evans, in which there was a function I wasn't familiar with.

user> (doc merge-with)
-------------------------
clojure.core/merge-with
([f & maps])
  Returns a map that consists of the rest of the maps conj-ed onto
  the first.  If a key occurs in more than one map, the mapping(s)
  from the latter (left-to-right) will be combined with the mapping in
  the result by calling (f val-in-result val-in-latter).

Although I get the general idea I think it's preferable to perform experiments after reading some documentation, just to be sure.

user> (merge-with #(+ %1 %2) {:a 1} {:a 2 :b 3} {:a 4 :b 5 :c 6})
{:c 6, :b 8, :a 7}
user> (merge-with #(list %1 %2) {:a 1} {:a 2 :b 3} {:a 4 :b 5 :c 6})
{:c 6, :b (3 5), :a ((1 2) 4)}

It does pretty much what the docs say, it's always great to find about a useful function you didn't knew about! Let's see some real code using this function.

(defn- merge-map
  "Merges an inner map in 'from' into 'to'"
  [to key from]
  (merge-with merge to (select-keys from [key])))

This was extracted from Compojure source code for response handling. It's used to merge the response's headers and session maps when the update-response multi-method is called on an object of the Map class. That gave me an idea: the principle of using merge as the with function could be generalized into a function that recursively merges maps. Let's to do it manually.

user> (merge {:a 1} {:a 2 :b 3} {:b 4 :c 5})
{:c 5, :b 4, :a 2}
user> (merge-with merge {:a {:a 1 :b 2}} {:a {:a 3 :c 4}} {:a {:b 5 :c 6}})
{:a {:c 6, :a 3, :b 5}}
user> (merge-with (partial merge-with merge) {:a {:a {:a 1} :b {:b 2}}} {:a {:a {:a 3} :c {:c 4}}} {:a {:b {:b 5} :c {:c 6}}})
{:a {:c {:c 6}, :a {:a 3}, :b {:b 5}}}

That's surely feasible, but I can't seems to find any use for such a function, so I'll leave it as an exercise. The complexity of this task certainly negate the actual benefits gained from its limited usefulness anyway. Is there anybody who disagree?

Update: Mister Bray posted yet another article on Clojure yesterday and proggit bursted in flames with relatively interesting (but heated) discussions on Clojure versus everything under the sun. It also provoked craziness!

No comments:

Post a Comment

About Me

My photo
Quebec, Canada
Your humble servant.