5

I have been writing some Clojure recently, and I found myself using the following pattern frequently enough:

(let [x (bam)
      y (boom)]
  {:x x
   :y y})

So I went ahead and wrote the following macro:

(defmacro make-keyword-map [& syms]
  `(hash-map ~@(mapcat (fn [s] [(keyword (name s)) s]) syms)))

With that, code now looks like:

(let [x (bam)
      y (boom)]
  (make-keyword-map x y)

Would this sort of macro be considered idiomatic? Or am I doing something wrong, and missing some already established pattern to deal with something of this sort?

missingfaktor
  • 90,905
  • 62
  • 285
  • 365

1 Answers1

7

Note, that you can also replace all of:

(let [x (bam) y (boom)] {:x x :y y})

with just:

{:x (bam) :y (boom)}

which will evaluate to the same thing.


If your let expressions depend on one another, then how about a macro like so:

(defmacro make-keyword-map [& let-body]
  (let [keywords-vals (flatten (map (juxt keyword identity)
                               (map first (partition 2 let-body))))]
    `(let ~(vec let-body)
       (hash-map ~@keywords-vals))))

that way (make-keyword-map x (foo 1) y (bar 2) z (zoom x)) expands to:

(clojure.core/let [x (foo 1) y (bar 2) z (zoom x)]
  (clojure.core/hash-map :x x :y y :z z))

So something like this would work:

user=> (defn foo [x] (+ x 1))
#'user/foo
user=> (defn bar [x] (* x 2))
#'user/bar
user=> (defn zoom [x] [(* x 100) "zoom!"])
#'user/zoom
user=> (make-keyword-map x (foo 1) y (bar 2) z (zoom x))
{:z [200 "zoom!"], :y 4, :x 2}

Not sure how idiomatic that is, but it also saves you a let, compared to your original example.

DJG
  • 6,413
  • 4
  • 30
  • 51
  • This is an overly simplified example. Often I need to use those local bindings to produce other local bindings. e.g. `(let [x (bam) y (boom x) z (zoom x)] ...)`. Because maps can't refer to other keys inside themselves (because they aren't "objects" in that sense) – missingfaktor Jan 03 '14 at 22:14
  • I think this new `make-keyword-map` is much cleaner. The original seemed to be operating at an awkward level of abstraction, where the definition of the map is intertwined with the names of local variables. – Chuck Jan 03 '14 at 23:09
  • 2
    Shorter version of same: `(defmacro make-keyword-map [& bindings] (let [ks (take-nth 2 bindings)] \`(let [~@bindings] (hash-map ~@(interleave (map keyword ks) ks)))))` – A. Webb Jan 04 '14 at 20:11