Macro that generates a defn I need to save nested data to disk for a project. Right now, my state is a nested record, and has proven to be a pain to serialize. I tried compressing it to a map, but after testing it, I realized my code failed in a unfortunate corner case that proved it to impractical. I decided to take the suggestion to drop using records altogether since I don't need the speed, but found it annoying to write -&gt;-ish pseudo-constructors for everything I needed to represent. I decided to write a macro that takes a vector of keyword fields, and emits a defn that creates an map with the field names mapped to the arguments of the function:

(defmap Hello [:one :two :three])
(-&gt;Hello 1 2 3)

{:one 1, :two 2, :three 3}


I decided for the macro to take keywords as arguments so they're introduced to IntelliJ. defrecord doesn't give any hints, so the first time you use a record's fields, they aren't suggested by IntelliJ.

Unfortunately, IntelliJ complains about Hello not existing, and doesn't recognize -&gt;Hello as a declared function, so this isn't very nice to use, but it was a fun little sidetrack. I don't know if I'll actually use it, but it's the most complicated macro I think I've ever written, and I'd like it reviewed.

I'd like to know if I'm doing anything roundabout or overly complicated. I'd also like to know if there's a way to hint to IntelliJ that Hello doesn't need to be defined, and that -&gt;Hello does.

(defmacro defmap
  "Generates a defn that creates a function with the name (str \"-&gt;\" map-name) that
    returns a map. The map will have keys of fields, with the values of the arguments passed to the function."
  [map-name fields]
  (let [adjusted-name# (str "-&gt;" map-name)
        args# (mapv (comp symbol name)  fields)
        body-map# (into {} (map #(do [% (symbol %2)]) fields args#))]

    `(defn ~(symbol adjusted-name#) ~args# ~body-map#)))
The code is nice, there are only some minor improvements here.


I love that you have written a docstring.
You do not need the # in the variable names. These names are used for generating symbols but you do not use this capability in the code.
You could make adjusted-make a symbol when defined in the let block. This way the constructed form would be simpler and more readable.
I prefer doing assert calls for error handling in macros. You could check that the first parameter is a symbol? and the second is a vector? of keys.
Instead of (into {} (map ...)) you can also use zipmap to construct the map.


The code can be rewritten as the following:

  (defmacro defmap
"Generates a defn that creates a function with the name (str \"-&gt;\" map-name) that
returns a map. The map will have keys of fields, with the values of the arguments passed to the function."
[map-name fields]
(assert (symbol? map-name))
(assert (vector? fields))
(let [adjusted-name (symbol (str "-&gt;" map-name))
      args          (mapv (comp symbol name) fields)
      body-map      (zipmap fields args)]
  `(defn ~adjusted-name ~args ~body-map)))