20 July 2010

Various forms of named parameters in Clojure, kind of...

The short of it, from Measuring Measures' Named Parameters in Clojure post is this:

user> (defn a [{b :b c :c}] (- b c))
#'user/a
user> (a {:c 5 :b 11})
6

But that's very repetitive for too many named parameters, so there's this other form too:

user> (defn a [{:keys [b c]}] (- b c))
#'user/a
user> (a {:c 5 :b 11})
6

That's very useful when there's just a lot of arguments to pass around. What if you want to keep all those arguments in a map to continue passing around? There's this option:

user> (defn a [{:keys [b c] :as args}] (- (:b args) c))
#'user/a
user> (a {:c 5 :b 11})
6

These are technically not named parameters at all, but a way to destructure maps passed as an argument to a function.

Caution is in order, however, as you should only destructure in the arg[ument] list if you want to communicate the substructure as part of the caller contract. Otherwise, destructure in a first-line let [Clojure Library Coding Standards], perhaps like so:

user> (defn a [argmap] (let [{:keys [b c]} argmap] (- b c)))
#'user/a
user> (defn a2 [argmap] (let [{b :b c :c} argmap] (- b c)))
#'user/a2
user> (a {:c 5 :b 11})
6
user> (a2 {:c 5 :b 11})
6

No comments: