Clojure Destructuring Gotcha!

Peter Lubell-Doughtie
December 04, 2015

Given the following function definition, what would you expect to happen if you ran (hello :person nil)?

(defn hello
      [& {:keys [person]
          :or {person "Rich"}}]
      (str "Hello, " person))

(hello) => "Hello, Rich"
(hello :person "Hickey") => "Hello, Hickey"
(hello :person nil) => "Hello, "

I’d have expected (hello :person nil) to have the same result as calling (hello), but as it turns out, Clojure seems to make a distinction between nothing and nil when it comes to destructuring.

A real world situation where this might occur would be where you, for instance, rely on the result of a destructuring operation to provide parameters for a function similar to hello. e.g.

(defn spam
      [& {:keys [person]}]
      (str (hello :person person)
            "Give me all your money."))

Calling (spam) would result in (hello :person nil) being called, which would have the – probably – unintended effect of returning "Hello, ". You may choose to add an :or when destructuring the argument to spam, but then you’ll have the same code appearing twice. A more localised solution would be changing the hello definition to be something like this.

(defn hello
      [& {:keys [person]}]
      (str "Hello, " (or person "Rich")))

This worked for my case, but I’d love to hear about more idiomatic approaches.

Tags