It looks like you want to write a file of delimited values using binding names and their values from inside a let
. Macros transform code during compilation and so they cannot know the run-time values that the symbols you pass are bound to. You can use a macro to emit code that will be evaluated at run-time:
(defmacro to-rows [& args] (let [names (mapv name args)] `(cons ~names (map vector ~@args)))) (defn get-stuff [] (let [nums [1 2 3] chars [\a \b \c] bools [true false nil]] (to-rows nums chars bools)))(get-stuff)=> (["nums""chars""bools"] [1 \a true] [2 \b false] [3 \c nil])
Alternatively you could produce a hash map per row:
(defmacro to-rows [& args] (let [names (mapv name args)] `(map (fn [& vs#] (zipmap ~names vs#)) ~@args)))=> ({"nums" 1, "chars" \a, "bools" true} {"nums" 2, "chars" \b, "bools" false} {"nums" 3, "chars" \c, "bools" nil})
You would then need to write that out to a file, either using data.csv or similar code.
To see what to-rows
expands to, you can use macroexpand
. This is the code being generated at compile-time that will be evaluated at run-time. It does the work of getting the symbol names at compile-time, but emits code that will work on their bound values at run-time.
(macroexpand '(to-rows x y z))=> (clojure.core/cons ["x""y""z"] (clojure.core/map clojure.core/vector x y z))
As an aside, I'm assuming you aren't typing thousands of literal values into let
bindings. I think this answers the question as asked but there could likely be a more direct approach than this.