Clojure: creating a macro to quote every symbols of a map - macros

I am trying to write a macro that takes a map as input, and that will quote every symbol that exists in that map (keys & values).
Here is what I would like to do. Let's say I have a macro quote-everything that take a map like this as input:
{foo "foo" :bar bar}
Then, evaluating:
(quote-everything {foo "foo" :bar bar})
Would produce:
{quote(foo) "foo" :bar quote(bar)}
Basically, I want the macro to generate a new map where every symbols are quoted. The problem is that I am don't know where to start, and if it is even possible. The thing is that the map can be anything, and the macro should support nested maps as well.
The detection of the symbols could be done using symbol?. However, to support nested maps the macro should probably call itself. But I have no idea how the new map should be created with such a macro.
Any pointers would be appreciated.

The quote special form already does this for you:
user> (= (quote {foo "foo" :bar bar})
{(quote foo) "foo" :bar (quote bar)})
;; => true
All quotation does is prevents a form from being evaluated. I think you might be making this harder than it needs to be.

clojure.walk/postwalk might help you here:
(require '[clojure.walk :as w])
(w/postwalk
(fn [x]
(if (symbol? x)
`(quote ~x)
x))
'{foo :foo :bar bar})
;; => {(quote foo) :foo, :bar (quote bar)}
Wrap that in a defmacro and it should get you started. Note that that really quotes every symbol, so if you have something like (inc 1) in there it will end up as ((quote inc) 1). That might actually be what you want, I just thought I'd mention it.

Related

Confusing clojure macro var evaluation

I am trying to write a clojure macro that transforms clojure keywords to java enums. But the evaluation of the parameters in a macro is confusing:
user=> (defmacro show-and-tell [thing]
#_=> `(vector ~(name thing) ~(type thing) ~thing))
#'user/show-and-tell
user=> (macroexpand-1 (show-and-tell :foo))
["foo" clojure.lang.Keyword :foo]
user=> (def foo :bar)
#'user/foo
user=> (name foo)
"bar"
user=> (type foo)
clojure.lang.Keyword
user=> (macroexpand-1 (show-and-tell foo))
["foo" clojure.lang.Symbol :bar]
So it works as I would expect if the keyword is provided directly as a parameter. But if it is a var, I don't get the correct name and type of the thingy.
I can make it 'sort-of-almost' work by using eval:
user=> (defmacro show-and-tell-with-eval [thing]
#_=> `(vector ~(name (eval thing)) ~(type (eval thing)) ~(eval thing)))
#'user/show-and-tell-with-eval
user=> (macroexpand-1 '(show-and-tell-with-eval foo))
(clojure.core/vector "bar" clojure.lang.Keyword :bar)
user=> (let [baz :bar-foo] (macroexpand-1 '(show-and-tell baz)))
(clojure.core/vector "baz" clojure.lang.Symbol baz)
user=> (let [baz :bar-foo] (macroexpand-1 '(show-and-tell-with-eval baz)))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: baz in this context
Can someone explain this to me? Is there no way to see the name of a (local) var inside a macro?
You might wanted to write
(defmacro show-and-tell [thing] `(vector (name ~thing) (type ~thing) ~thing))
Ad-hoc explanation:
The key to understand what's going on is to know when the arguments are evaluated. Macros take unevaluated data structures as arguments and return a data structure which is then evaluated using the rules above. Using the ~ you tell the compiler which data structures should be evaluated at runtime, thus, your thing argument, not the return value of (name thing) value as the thing value will be bound at compile time in the latter case which is not what you wanted
Here you have a further explanation about writing macros http://www.braveclojure.com/writing-macros/
You seem to be confused about the relation between vars and what they contain and how macros come into play. "Vars provide a mechanism to refer to a mutable storage location" (cf. the offical docs on Vars). When you evaluate foo in the REPL, Clojure will evaluate it according to the rules outlined in the offical docs for evaluation which in this case means, it resolves the symbol to "the value of the binding of the global var named by the symbol".
Now, it's crucial to understand that macros "are functions that manipulate forms, allowing for syntactic abstraction". Basically macros allow direct access to any parameter handed to the macro and then manipulate and evaluate the related data as needed. Let's take a glance at your macro and what happens in your "erroneous" case:
(defmacro show-and-tell [thing]
`(vector ~(name thing) ~(type thing) ~thing))
Your macro definition gets fed some thing (whatever is the parameter to show-and-tell). At this point, thing will not be resolved. Only in your macro definition you have some evaluations. Please note that in this call, you're calling macroexpand-1 on the (evaluated) result of (show-and-tell foo) which is probably not what you want:
user=> (macroexpand-1 (show-and-tell foo))
["foo" clojure.lang.Symbol :bar]
Quoting the call shows what's going on:
user=> (macroexpand-1 '(show-and-tell foo))
(clojure.core/vector "foo" clojure.lang.Symbol foo)
You're calling vector with "foo" (i.e. the name of foo) where foo is a symbol and then your code will resolve foo normally (and give :bar).
From your description, you seem to expect that normal symbol resolution will take place for all your arguments. If this is what you want, you don't need a macro in the first place. But just for the record, by now it should be obvious what you need to do: you need to evalute thing first (which is pretty much what you did with eval). In other words, you placed your unquote operator wrong:
(defmacro show-and-tell [thing]
`(vector (name ~thing) (type ~thing) ~thing))
user=> (macroexpand-1 '(show-and-tell foo))
(clojure.core/vector (clojure.core/name foo) (clojure.core/type foo) foo)
user=> (show-and-tell foo)
["bar" clojure.lang.Keyword :bar]

Nested quotion and expanding

Lets say i have fn begin: (begin "hello"). The result of `(begin "hello") will be (my-ns/begin "hello"). It's good. But now I do this: (def x '(begin "hello")). How can I expand x with backquote to get (my-ns/begin "hello"), not just (begin "hello")?
In the first example you used ` which is a reader macro named "syntax-quote", In the second example you used ' Which is a reader macro named "quote". Syntax-quote offers a couple of features beyond what quote offers:
unquoting with ~, ~#, etc
namespace expansion of symbols for use in writing hygenic macros.
Plain old quote does neither of these. So if you want namespace expansion in both examples use ` in both places. If you want to get an un-namespaced symbol in a syntax-quooted list you can use both of them together `(println ~'symbol-name) which will evaluate to simply (println symbol-name). (in this case symbol-name needs to be defined in the namespace where this is called (this process in known as "symbol capture")). The syntax-quote evaluates first to a call to quote, when in turn evaluates to the symbol.
If you are looking to expand a symbol that already exists for some reason, or you want to expand it in several different namespaces that both define it, you can use the ns-resolve function:
(ns-resolve *ns* 'begin)
so in your example you can map this over the list to print it with the NS qualified symbols:
user> (map #(if (symbol? %) (ns-resolve *ns* %) %) x)
(#'user/begin "hello")
Though this is not exactly the desired output because it refers to the var in that namespace rather than the symbol that resolves to this var. Things get a little dicey here because ` is a reader macro and we can't build calls to it in other macros. For instance I can think of no way to write:
(defmacro expand-x [thing]
`(syntax-quote-here ~thing)
Because as far as I know syntax-quote doesn't have a name (again because it is a reader macro) so there is nothing to use in place of syntax-quote-here in the above example. Though never fear, in a language with eval nothing is impossible, things just get arbitrarily ugly:
user> (eval (read-string (str "`" x)))
(user/begin "hello")
PS: don't actually use this last example or gremlins will inhabit your code for all time

lisp macro expand with partial eval

I have following code which confuse me now, I hope some can tell me the difference and how to fix this.
(defmacro tm(a)
`(concat ,(symbol-name a)))
(defun tf(a)
(list (quote concat) (symbol-name a)))
I just think they should be the same effect, but actually they seem not.
I try to following call:
CL-USER> (tf 'foo)
(CONCAT "FOO")
CL-USER> (tm 'foo)
value 'FOO is not of the expected type SYMBOL.
[Condition of type TYPE-ERROR]
So, what's the problem?
What i want is:
(tm 'foo) ==> (CONCAT "FOO")
The first problem is that 'foo is expanded by the reader to (quote foo), which is not a symbol, but a list. The macro tries to expand (tm (quote foo)). The list (quote foo) is passed as the parameter a to the macro expansion function, which tries to get its symbol-name. A list is not a valid argument for symbol-name. Therefore, your macro expansion fails.
The second problem is that while (tm foo) (note: no quote) does expand to (concat "FOO"), this form will then be executed by the REPL, so that this is also not the same as your tf function. This is not surprising, of course, because macros do different things than functions.
First, note that
`(concat ,(symbol-name a))
and
(list (quote concat) (symbol-name a))
do the exact same thing. They are equivalent pieces of code (backquote syntax isn't restricted to macro bodies!): Both construct a list whose first element is the symbol CONCAT and whose second element is the symbol name of whatever the variable A refers to.
Clearly, this only makes sense if A refers to a symbol, which, as Svante has pointed out, isn't the case in the macro call example.
You could, of course, extract the symbol from the list (QUOTE FOO), but that prevents you from calling the macro like this:
(let ((x 'foo))
(tm x))
which raises the question of why you would event want to force the user of the macro to explicitly quote the symbol where it needs to be a literal constant anyway.
Second, the way macros work is this: They take pieces of code (such as (QUOTE FOO)) as arguments and produce a new piece of code that, upon macroexpansion, (more or less) replaces the macro call in the source code. It is often useful to reuse macro arguments within the generated code by putting them where they are going to be evaluated later, such as in
(defmacro tm2 (a)
`(print (symbol-name ,a)))
Think about what this piece of code does and whether or not my let example above works now. That should get you on the right track.
Finally, a piece of advice: Avoid macros when a function will do. It will make life much easier for both the implementer and the user.

In clojure, how can I evaluate the arguments to a macro from another macro?

I have two macros. The first one takes a symbol as the only parameter (because it's passed to def, which needs a symbol). The second function takes a list of symbols and should call the first with each symbol individually.
(defmacro m1 [s]
'(let [f# ... dynamic function definition ...]
(def ~s f#))
The second macro should take a list of symbols and pass them to the first, but I can't get it to work. The best I could come up with was the following:
(defmacro m2 [symbols]
`(for [s# ~symbols] (eval (read-string (str "(name.of.current.namespace/m1 " s# ")")))))
which forces the s# to be evaluated before it is passed to the first macro. It is also invoked with a list of strings, rather than a list of symbols.
This is useful for a library which I am using for which all of the functions in the library takes the same two first parameters. I am trying to create wrapper functions, in my namespace, for some of the functions, which automatically provides the first two parameter values which are common to all of them.
Any ideas to improve this?
Usually when you ask how to get two macros to cooperate, the answer is to not make them both macros. I think my blog post on macro-writing macros will help clarify. For this particular situation I'd probably combine two suggestions from the comments:
(defmacro build-simpler-functions [& names]
(cons 'do
(for [name names]
`(def ~(symbol (str "simple-" name))
(partial ~name 5 10))))) ; if you always pass 5 and 10
(build-simpler-functions f1 f2)
This expands to
(do
(def simple-f1 (clojure.core/partial f1 5 10))
(def simple-f2 (clojure.core/partial f2 5 10)))
which looks like basically what you want.
Edit: if the args you "always" pass are different for each function, you can do this:
(defmacro build-simpler-functions [& names]
(cons 'do
(for [[name & args] names]
`(def ~(symbol (str "simple-" name))
(partial ~name ~#args)))))
(build-simpler-functions [f1 5 10] [f2 "Yo dawg"]) ; expansion similar to the previous

Splicing unquote for non-literal sequences

This question is a based on a limitation of this answer.
If I have a macro that uses splicing unquote like this:
(defmacro instantiate [klass values]
`(new ~klass ~#values))
It will only work if values is a literal sequence or seq-able.
If it is passed a var holding a sequence like:
(def v [1 2 3])
(macroexpand '(instantiate Person v))
Then the output would be an error indicating that v is not a sequence.
Even a function call would be interpreted as a list:
(defn vf [] [1 2 3])
(macroexpand '(instantiate Person (vf)))
user=>(new Person vf)
My question is: Is there any way to use the splicing unquote in Clojure macros in those two cases where the sequence to be spliced isn't a literal?
Macros receive their arguments unevaluated, so the behavior you are seeing is as intended.
Macros are expanded at compile time, not run time. The values of any variables passed into a macro may not be available at compile time, so dirty hacks like using eval will not work in the general case. Don't create macros that require such tricks.
splicing quotes save a lot of time in almost all cases, except when they dont work, then you need to do things the old fashioned way...
(defmacro instantiate [klass values]
`(new ~klass ~#values))
could become
(defmacro instantiate [klass values]
(concat (list 'new klass) (if (seq? values)
values
(list values))))
user=> (macroexpand '(instantiate asdf (1 2 3)))
(new asdf 1 2 3)
user=> (macroexpand '(instantiate asdf 1))
(new asdf 1)