Understanding symbol resolution with macros in Clojure - clojure

I'm learning macros in clojure. I need help with symbol resolution in macros.
(ns macros.testing)
(def no (rand-int 10))
(defmacro drawer []
`(do
~#(for [i (range no)]
`(print ~i))))
(drawer) ;; This can resolve the global variable `no` properly
(defmacro drawer-2 [n]
`(do
~#(for [i (range n)]
`(print ~i))))
(drawer-2 10) ;; This works fine too
(drawer-2 n) ;; This is not working, I'm unable to pass the global variable as an argument to the macro, although it's visible in the other case
The error message is class clojure.lang.Symbol cannot be cast to class java.lang.Number (clojure.lang.Symbol is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap').
Why clojure is unable to resolve the global variable in the second case and why can it do so for the first macro?

When a macro is evaluated, its arguments are passed "as is", without symbol resolution, as if they're all quoted. So if you pass n, within a macro it'll be a symbol n and not a value contained in a relevant var. If you pass (inc 10), it'll be '(inc 10). And so on.
So in drawer, (range no) works because no refers to a var that's bound to the result of (rand-int 10).
In drawer-2, the argument n has the value of (symbol "n") (or just 'n) during macro evaluation. And (range 'n) can't work - that's exactly what that "can't cast a symbol to a number" exception that you saw is about.
To fix that, you can explicitly resolve the passed symbol by using resolve (that # is needed to get the value of the resolved var):
(def value 10)
(defmacro drawer-2 [n]
(let [n (if (symbol? n)
#(resolve n)
n)]
`(do
~#(for [i (range n)]
`(print ~i)))))
(drawer-2 value)

Related

working with non-namespaced symbols in clojure

Here's a working minimal example showing how Clojure can handle non-namespaced symbols:
(defmacro simple-macro [s]
(name `~s))
(str "And the answer is "
(simple-macro v1))
Now I'd like to do something more complicated. Inspired by this example:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]
I now want to define a similar closure to take the names of undefined variables.
(defn take-names-fun []
(let [names (atom [])]
#((swap! names conj (simple-macro %)) (deref names))))
(def P (take-names-fun))
(P v1)
But this doesn't work as hoped; I get the error:
Unable to resolve symbol: v1 in this context
Is there a way to fix this so that we can add the name "v1" to the list of names defined above?
I tried using a macro instead (inspired by a syntax trick on page 21 of "Mastering Clojure Macros")... but this answer on ask.clojure.org says it doesn't make sense to define a closure over an atom in a macro.
(defmacro take-names-macro []
(let [names (atom [])]
`(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))
(def R (take-names-macro))
And indeed, I get another error here:
Can't embed object in code, maybe print-dup not defined:
However, there is no such restriction for using atoms inside defn. Maybe at the end of the day I need to put my symbols in a namespace...?
Not quite sure what it is that you're ultimately trying to accomplish.
But, since P is a function, it will always evaluate its arguments. So, if you pass it an undefined symbol, you'll get the error you got. Instead, you have to create a macro so that you can quote the undefined symbol (to stop the evaluation of the argument) and then pass that to P. Here is an example that does that.
user> (defn take-names-fun []
(let [names (atom [])]
(fn [arg] (swap! names conj (name arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user>
You might find the article on Evaluation in Clojure helpful.
#dorab's answer is nice.
But you could also tell yourself: "When entering undefined variables into a function, I have to quote them to avoid evaluation of them!"
So, after:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
Do:
user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=>
In this way you don't need the macro and you can alternate between evaluated and not-evaluated arguments (undefined symbols).
So with the way fn's are written in clojure there is unfortunately no way to get the name of the var being passed as a param from within the fn body.. Someone with more experience with the clojure src may be able to explain better why that is, my initial guess would be that it has something to do with keeping thread local scopes isolated and lazy.
But there's absolutely nothing stopping you from writing a macro that wraps other macros using your closure idea!
Here's an example of how something like that may be written:
https://stackoverflow.com/a/11857444

How to write an Clojure macro to get var's value?

user=> (def v-1 "this is v1")
user=> (def v-2 "this is v2")
user=> (defmacro m [v] (symbol (str "v-" v)))
user=> (m 1)
"this is v1"
user=> (m 2)
"this is v2"
user=> (let [i 2] (m i))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: v-i in this context, compiling:(NO_SOURCE_PATH:73:12)
Can I write a macro let both
(m 2)
and
(let [i 2] (m i))
get "this is v2" ?
This is possible without a macro:
(defn m [v] (var-get (resolve (symbol (str "v-" v)))))
(m 1) ;; => "This is v1"
(let [i 2] (m i)) ;; => "This is v2"
You can use a macro too if you want:
(defmacro m [v] `#(resolve (symbol (str "v-" ~v))))
A plain function seems much more likely to be what you want.
First, though, to address the original question, if you wanted to insist on using a macro, macros are regular functions that happen to be called at compile time, so you can look up a Var using its symbolic name and obtain its value using deref just like you could at (your application's, as opposed to your macro's) runtime:
(defmacro var-value [vsym] #(resolve vsym))
(def foo 1)
(var-value foo)
;= 1
(macroexpand-1 '(var-value foo))
;= 1
Note that the above 1 is the actual macroexpansion here. This is different to
(defmacro var-value [vsym] `#(resolve ~vsym))
in that the latter expands to a call to resolve, and so the lookup given that implementation is postponed to your app's runtime.
(macroexpand-1 '(var-value foo))
;= (clojure.core/deref (clojure.core/resolve foo))
So this code will just be inlined wherever you call the macro.
Of course the macro could also expand to a symbol – e.g.
(defmacro prefixed-var [suffix]
`(symbol (str "v-" ssuffix)))
will produce expansions like v-1 (for (prefixed-var 1)) etc.
Going back to the subject of the suitability of macros here, however, if you use a macro, all the information that you need to produce your expansion must be available at compile time, and so in general you cannot use the values of let / loop locals or function arguments in your expansion for the fundamental reason that they don't have any fixed value at compile time.1
Thus the cleanest approach would probably be to wrap a resolve call in defn and call the resulting function – although of course to know for sure, we'd need to know what problem you were trying to solve by introducing a macro that performs a Var lookup.
1 Except if statically assigned constant values, as in the example given in the question text; I'm assuming you're thinking of using runtime values of locals in general, not just those that whose initialization expressions are constant literals.

Implementing until as a macro

Here's my failed attempt:
(defmacro until
[condition body setup increment]
`(let [c ~#condition]
(loop [i setup]
(when (not c)
(do
~#body
(recur ~#increment))))))
(def i 1)
(until (> i 5)
(println "Number " i)
0
(inc i))
I get: CompilerException java.lang.RuntimeException: Can't let qualified name: clojure-noob.core/c
I am expecting this output:
Number 1
Number 2
Number 3
Number 4
Number 5
What's wrong?
There are a few issues with the macro:
You need to generate symbols for bindings inside macros. A convenient way to do this is suffix the names with #. Otherwise the bindings in your macros could overshadow bindings elsewhere in your code.
Some of the macro inputs were unnecessarily spliced when unquoted i.e. ~# instead of ~
Here's a version of the macro that will compile/expand:
(defmacro until [condition body setup increment]
`(let [c# ~condition]
(loop [i# ~setup]
(when-not c#
~body
(recur ~increment)))))
But this will loop forever in your example because condition is only evaluated once and i's value would never change anyway. We could fix that:
(defmacro until [condition body increment]
`(loop []
(when-not ~condition
~body
~increment
(recur))))
And we need to make i mutable if we want to change its value:
(def i (atom 1))
(until (> #i 5)
(println "Number " #i)
(swap! i inc))
;; Number 1
;; Number 2
;; Number 3
;; Number 4
;; Number 5
But now until is starting to look a lot like the complement of while, and its extra complexity doesn't seem beneficial.
(defmacro until [test & body]
`(loop []
(when-not ~test
~#body
(recur))))
This version of until is identical to while except the test is inverted, and the sample code above with the atom still behaves correctly. We can further simplify until by using while directly, and it'll ultimately expand to the same code:
(defmacro until [test & body]
`(while (not ~test) ~#body))
Change the let line too:
...
`(let [c# ~#condition]
...
Then rename all references of c to c#. The postfix # generates a unique, non-namespaced-qualified identifier to ensure that the symbol created by the macro doesn't clash with any existing symbols in the context that the macro expands into. Whenever you bind a symbol in a quoted form, you should be using # to prevent collisions, unless you have a good reason to not use it.
Why is this necessary in this case? I can't remember exactly the reason, but if I recall correctly, any symbols bound in a syntax quoted form (`()) are namespace qualified, and you can't use a let to create namespace qualified symbols.
You can recreate the error by typing:
(let [a/a 1]
a/a)

how to apply gensym to each specific variable

I want to write a macro (my-dotimes [x init end] & body) that computes the value of body for x going from init to end-1 in increments of 1. Here you again have to make sure to avoid the "variable capture problem". It should work like this:
user=> (my-dotimes [x 0 4] (print x))
0123nil
my code is :
(defmacro my-dotimes [[x initial end] & body]
`(loop [i# ~initial]
(when (< i# ~end)
~#body
(recur (inc i#))))))
but when I use macroexpand to check it and find:
user=> (macroexpand '(my-dotimes [x 0 4] (println x)))
(loop* [i__4548__auto__ 0] (clojure.core/when (clojure.core/<i__4548__auto__ 4)
(println x)
(recur (clojure.core/inc i__4548__auto__))))
I am wondering how to change
(println x) => (clojure.core/println i__4548__auto__)
Here, you supply the symbol that should be bound to the counter (here x), so you don't need to use gensyms.
Instead of using i#, just introduce the symbol given to you by the user of the macro.
You need gensyms when you introduce new symbols and don't want them to collide with existing symbols.
In Common Lisp, it would make sense to wrap the body with a binding from the user-supplied symbol to the current value of i, using (let ((,x ,i)) ,#body), because the user's code could change the value of the counter during iteration (which could be bad). But here I think you cannot mutate the variable directly, so you don't need to worry about that.
Your second example is:
(defmacro for-loop [[symb ini t change] & body]
`(loop [symb# ~ini]
(if ~t
~#body
(recur ~change))))
First problem: when you expand the body, which might be one or more form, you'll end-up with an if form with many branches instead of 2. You would have for example (if test x1 x2 x3 (recur ...)) if your body contains x1, x2 and x3. You need to wrap bodies in do expressions, with (do ~#body).
Now, the situation is not very different than before: you still have a symbol, given by the user, and you are responsible for establishing the bindings in the macro. Instead of using symb#, which creates a new symbol, completely distinct from symb, just use symb directly.
You could do this for example (untested):
(defmacro for-loop [[symb init test change] &body]
`(loop [~symb ~init]
(if ~test (do ~#body) (recur ~change))))
As long as you use the symbol provided by the caller of your macro, gensyms are not necessary. You need gensyms when you have to create a new variable in the generated code, which requires to have a fresh symbol. For example, you evaluate an expression only once and need a variable to hold its value:
(defmacro dup [expr]
`(let [var# ~expr]
[var# var#]))

IllegalStateException in nested quote and unquote

Here is an example from joy of clojure:
(let [x 9, y '(- x)]
(println `y)
(println ``y)
(println ``~y)
(println ``~~y))
Output from repl:
typedclj.macros/y
(quote typedclj.macros/y)
typedclj.macros/y
(- x)
If I rearrange the order of quote/unquote a bit, results are still the same (I am wondering why):
(let [x 9, y '(- x)]
(println `y)
(println ``y)
(println `~`y)
(println `~`~y))
But if I put the tilde in front:
(let [x 9, y '(- x)]
(println `y)
(println ``y)
(println `~`y)
(println ~``~y))
I get a strange error:
CompilerException java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.core/unquote, compiling:(/Users/kaiyin/personal_config_bin_files/workspace/typedclj/src/typedclj/macros.clj:1:25)
Why do I get this error?
Short answer: you're trying to unquote outside of a syntax-quote, and that doesn't make sense.
More details:
This error is generated from the final println. Observe that
(println ~``~y)
expands to
(println (unquote (syntax-quote (syntax-quote (unquote y))))
This happens by the virtue of ~ and the backtick character being reader macros. The expansion unquote is not actually a normal function or a macro. It's a special form which is defined only inside of a syntax-quote. You can see this in the compiler source, in LispReader.java. When you use it outside of a syntax-quote form, the reader macro has still happened but there's no such function as 'unquote'. There is only a bare (def unquote) in core.clj (the very first definition).
When you do a def like that, you end up with a var whose initial binding is an instance of the class cloure.lang.Unbound (it's one of the constructors on clojure.lang.Var. This subclasses clojure.lang.AFn but doesn't specify any arities; so every invocation of it as a function calls throwarity, giving you this exception.