Reputation: 6018
I've been trying to get this to work with quote, quote-splicing, eval, and whatever else I can think of, but no luck so far. I understand why it doesn't work - it's being seen as a map, and it's trying to eval a
, b
, and c
- just not how to get around it.
(def destructor {a :a b :b c :c})
; CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(:1:15)
(let [destructor my-map]
'etc)
I have a rather involved destructuring map that I'm considering using several times, so it seemed a good idea to tuck it away somewhere. Maybe there are better ways to go about it?
Upvotes: 5
Views: 150
Reputation: 26436
You'll need to quote the destructing pattern
(def my-destructor '{a :a b :b c :c})
You can do this with levels of quoting/unquoting, but it is easier to see with a little helper function.
(defn- with-destructor* [binding & body]
`(let ~binding ~@body))
This eval
occurs at "compile" time (during macro expansion)
(defmacro with-destructor [[destructor form] & body]
(eval `(with-destructor* [~destructor '~form] '~@body)))
As shown
(macroexpand-1 '(with-destructor [my-destructor {:a 1 :c 3}] (+ a c)))
;=> (clojure.core/let [{a :a, b :b, c :c} {:a 1, :c 3}] (+ a c))
Result
(with-destructor [my-destructor {:a 1 :c 3}] (+ a c))
;=> 4
Upvotes: 0
Reputation: 3378
Something like @amalloy's answer was my first instinct too and it's probably the way to go. That said, it might be worth considering a plain ol' higher-order function:
(defn destruct
[{a :a b :b c :c} f]
(f a b c))
(destruct my-map
(fn [a b c]
(println a)
(println b)
(println c)))
It's a little nosier and you're forced to name the bindings every time, but you avoid potential hygiene issues and, depending on your level of comfort with metaprogramming, it's a little easier to put together.
Upvotes: 2
Reputation: 91837
Good idea, but you can't do it in quite this way, because the thing you want to store is not really a value, so you can't store it in a var.
Instead, you can define a macro that includes this in its expansion:
(defmacro with-abc [abc & body]
`(let [~'{:keys [a b c]} ~abc]
~@body))
(with-abc foo
(...use a, b, and c...))
Upvotes: 3