I have a symbol "a" bound to a function:
(defn a []
(println "Hello, World"))
user=> a
#<user$a__292 user$a__292@97eded>
user=> (a)
Hello, World
nil
Then I use syntax-quote, it “resolves the symbol in the current context, yielding a fully-qualified symbol”, according to Clojure documentation. But why can’t I use it the same way as unqualified symbol?
user=> `a
user/a
user=> (`a)
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
Second question: if I have a symbol in a list, why can’t I evaluate it the same way as if I would evaluate the symbol directly?
user=> (def l '(a 1 2))
#'user/l
user=> 'l
l
user=> (first l)
a
user=> ((first l))
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
I have a suspicion I have a fatal flaw somewhere in the fundamental understanding of how symbols work here. What is wrong with above code?
REPL = read eval print loop. Step through the read-eval process.
READ: Clojure sees the string
"(`a)", parses it and ends up with a data structure. At read time, reader macros are expanded and not much else happens. In this case, the reader expands the backquote and ends up with this:EVAL: Clojure tries to evaluate this object. Evaluation rules vary depending on what kind of object you’re looking at.
Refer to
clojure.lang.Compiler/analyzeSeqin the Clojure source to see the evaluation rules for lists, orclojure.lang.Compiler/analyzeSymbolfor symbols. There are lots of other evaluation rules there.Example
Suppose you do this:
The REPL ends up doing this internally:
Clojure sees that you’re evaluating a list, so it evaluates all items in the list. The first (and only) item:
ais not a special form and this list doesn’t need to be macroexpanded, so the symbolais looked up in the namespaceuserand the resulting value here is anfn. So thisfnis called.Your code
But instead you have this:
Clojure evaluates the first item in the list, which is itself a list.
It evaluated the first item in this sub-list,
quote, which is a special form, so special rules apply and it returns its argument (the Symbola) un-evaluated.The symbol
ais the value in this case as thefnwas the value up above. So Clojure treats the Symbol itself as a function and calls it. In Clojure, anything that implements theIfninterface is callable like anfn. It so happens thatclojure.lang.SymbolimplementsIfn. A Symbol called as a function expects one parameter, a collection, and it looks itself up in that collection. It’s meant to be used like this:This is what it tries to do here. But you aren’t passing any parameters, so you get the error “Wrong number of args passed to: Symbol” (it expects a collection).
For your code to work you’d need two levels of
eval. This works, hopefully you can see why:Note that in real code, using
evaldirectly is usually a really bad idea. Macros are a better idea by far. I’m only using it here for demonstration.Look in
Compiler.javain the Clojure source to see how this all plays out. It’s not too hard to follow.