编写多元数defn宏时遇Clojure规范报错,求排查方案
defbasic Macro Issue with Clojure Spec Let's break down the potential issues with your macro and how to fix them, since you suspect the multi-arity function might be part of the problem (and there are a couple other common gotchas here too):
First, Check Macro Expansion
Before diving into fixes, always inspect what your macro actually generates—this is the #1 step for debugging Clojure macros. Run this in your REPL:
(macroexpand-1 '(defbasic :cat))
This will show you the exact defn form being created, which often reveals subtle issues you might miss in the macro source.
Likely Issue 1: Compile-Time vs Runtime Data Access
Your current macro fetches options at compile time (when the macro runs) via (get basic-builders val-fn). If basic-builders is populated at runtime (e.g., loaded from a config, or modified after the macro expands), options will be nil when the generated function runs. This almost certainly triggers a Clojure Spec error, since ->basic-operation expects a valid options map, not nil.
Fix: Delay Options Lookup to Runtime
Move the get call inside the generated function so it runs when the function is invoked, not when the macro expands:
(defmacro defbasic [val-fn] (let [;; Ensure we generate a symbol, not a string, for the function name fn-name (symbol (fdat/->string "make-" val-fn))] `(defn ~fn-name ([] (~fn-name {})) ([descriptor] (->basic-operation (get basic-builders ~val-fn) descriptor)))))
Likely Issue 2: Function Name is a String, Not a Symbol
If fdat/->string returns a string like "make-cat" instead of a symbol 'make-cat, your generated defn will try to use a string as a function name—which is invalid Clojure syntax. The fix above adds (symbol ...) to convert the string output to a valid symbol that defn can use.
Bonus: Add Error Handling for Missing Options
To make debugging easier later, add a check for missing options so you get a clear, actionable error instead of a vague Spec failure:
(defmacro defbasic [val-fn] (let [fn-name (symbol (fdat/->string "make-" val-fn))] `(defn ~fn-name ([] (~fn-name {})) ([descriptor] (let [options# (get basic-builders ~val-fn)] (if (nil? options#) (throw (IllegalArgumentException. (str "No builder configuration found for: " ~val-fn))) (->basic-operation options# descriptor)))))))
The options# uses an auto-gensym to avoid symbol capture issues in the macro.
Verify the Fix
After updating the macro, re-run (defbasic :cat) and test (make-cat) or (make-cat {:some :descriptor}). The Spec error should disappear if the root cause was the compile-time options lookup or invalid function name.
内容的提问来源于stack exchange,提问作者Joseph Yourine

