0
votes

I want to convert {a 1, b 2} clojure.lang.PersistentArrayMap into [a 1 b 2] clojure.lang.PersistentVector in clojure.

I have tried to write a function in clojure which converts {a 1, b 2} into [a 1 b 2]. I have also written a macro which gives me expected end result. In clojure we cannot pass the values generated inside functions to macros. For that I wanted to know a way in which I can implement a macro directly which can convert {a 1, b 2} into (let [a 1 b 2] (println a)), which will return 1.

Dummy Macro:

(defmacro mymacro [binding & body]
--some implemetation---)

Execution :

(mymacro '{a 1, b 2} (println a))

output:

1
nil

My Implementaion:

Function which converts into desired output.

(defn myfn [x]
 (let [a (into (vector) x) b (vec (mapcat vec a))]  b))

Execution:

(myfn '{a 1, b 2})

Output:

[a 1 b 2]

MACRO:

(defmacro list-let [bindings & body] `(let ~(vec bindings) ~@body))

Execution:

(list-let [a 1 b 2] (println a))

Output:

1
nil

I wanted to know how can I implement the same inside the macro itself and avoid the function implementation to get the require output. Something same as dummy macro given above. I am also interested in knowing if there is any which through which I can pass the value from my funtion to the macro without using (def)

2

2 Answers

3
votes

in general, macro code is plain clojure code (with the difference that it returns clojure code to be evaluated later). So, almost anything you can think of coding in clojure, you can do inside macro to the arguments passed.

for example, here is the thing you're trying to do (if i understood you correctly):

(defmacro map-let [binding-map & body]
  (let [b-vec (reduce into [] binding-map)]
    `(let ~b-vec ~@body)))

(map-let {a 10 b 20} 
  (println a b) 
  (+ a b))
;;=> 10 20
30

or like this:

(defmacro map-let [binding-map & body]
  `(let ~(reduce into [] binding-map) ~@body))

or even like this:

(defmacro map-let [binding-map & body]
  `(let [~@(apply concat binding-map)] ~@body))
1
votes

You don't need a macro for this, and you should always prefer functions over macros when possible.

For your particular case, I have already written a function keyvals which you may find handy:

(keyvals m)
 "For any map m, returns the keys & values of m as a vector,
  suitable for reconstructing via (apply hash-map (keyvals m))."

(keyvals {:a 1 :b 2})
;=> [:b 2 :a 1]
(apply hash-map (keyvals {:a 1 :b 2}))
;=> {:b 2, :a 1}

And, here are the full API docs.


If you are curious about the implementation, it is very simple:

(s/defn keyvals :- [s/Any]
  "For any map m, returns the (alternating) keys & values of m as a vector, suitable for reconstructing m via
   (apply hash-map (keyvals m)). (keyvals {:a 1 :b 2} => [:a 1 :b 2] "
  [m :- tsk/Map ]
  (reduce into [] (seq m)))