Welcome! Please see the About page for a little more info on how this works.

0 votes
ago in Macros by

The following code crashes:

(defmacro m []
  `(fn [x]))

(macroexpand '(m))

The error is:

Call to clojure.core/fn did not conform to spec
:reason "Extra input"

The following code does not crash and works as expected:

(defmacro m []
  `(asdfasdf [x]))

(macroexpand '(m))

How does the first snippet crash when the macro should just be returning a quoted piece of code? Why does it run fn at all?

2 Answers

+1 vote
ago by
selected ago by
Best answer

Not sure what version of Clojure or editor you're using but Clojure 1.10.3 gives more useful output in the standard repl:

user=> (macroexpand '(m))
Syntax error macroexpanding clojure.core/fn at (REPL:1:1).
(user/x) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
user/x - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list

As you can see it is not running your code but (recursively) macroexpanding when the problem is encountered. The first expansion will become:

(clojure.core/fn [user/x])

clojure.core/fn is itself a macro that is expanded. During that expansion, the fn macro is checked against the fn spec (this is on by default for macros). The spec finds user/x and sees it as "extra input" in the :params because it does not conform to any expected parameter form (unqualified symbols usually, but also all recursive destructuring forms). Really, the problem here is user/x instead of x.

You can fix this with:

(defmacro m []
  `(fn [~'x]))
ago by
reshown ago by
Much appreciated, that does make sense.
ago by
I don't get why x evaluated to user/x when it's syntax quoted though. How is that different from unquoting and then quoting again?
ago by
Syntax quote resolves all symbols in terms of the current namespace context. Unquote turns on evaluation, then evaluated a quoted unresolved symbol.
ago by
I see thanks again
+1 vote
ago by

Try macroexpand-1 instead - then the mistake should become obvious.