Clojure beginner, trying macros. I'm writing the following macro
(defmacro f [exp]
(let [[a op b] exp]
(list op a b)))
(f (1 + 2))
which works as intended.
However
I tried to replace the returned value from (list op a b) to '(op a b) and I get *unable to resolve symbol op in this context. I figured that the error is caused because list evaluates its arguments first, so I tried with '(~op a b), but still get the same error. What am I understanding wrong?
The problem is that op, a, b can not be evaluated inside a quoted form. You need to use the backtick symbol instead of ' (single quote) if you want to use ~ (unquote) inside a macro.
(defmacro f [exp]
(let [[a op b] exp]
`(~op ~a ~b)))
Related
I can "generate" a def with a macro.
(defmacro my-def [my-name]
`(def ~my-name 42))
(my-def a)
a; => 42
If I try to do something similar with a list
(defmacro my-defs [my-names]
`(do
~#(for [name# my-names]
`(def ~name# 42))))
(my-defs (a b c))
(macroexpand '(my-defs (a b c))); => (do (def a 42) (def b 42) (def c 42))
It works as long as I use a literal list as input. But as soon as I want to pass in a var
(def my-list '(a b c))
(macroexpand '(my-defs my-list)); => Don't know how to create ISeq from: clojure.lang.Symbol
I struggle to access the value of my-names. I can't use ~my-names as it is already used in a unquote-splice (~#) and would lead to an "Attempt[...] to call unbound fn".
What am I missing?
Do I need to use (var-get (resolve my-names))?
Do macros in these cases need to "detect" if the passed argument is a literal value or a var and act accordingly in order to work for both?
Or is it idiomatic to use eval to avoid this?
Addressing #Alan Thompson's question "[...] why [do] you want to do this?": I have a specification (a deeply nested map) of "resources" and it would be rather handy to have a macro generate defs (records) for these resources in order to use them down the line. So I guess no reason out of the ordinary "It would DRY up things". :) At this time I found a way by wrapping my-names in an eval. The question that remains is: Is this idiomatic, or is there a better way?
generally you can't employ macro to generate code based on runtime value,
still your task doesn't require macro in clojure, since you can dynamically intern vars in namespaces:
(defn intern-vals [data]
(doseq [[var-name var-val] data]
(intern *ns* var-name var-val)))
user> (intern-vals {'some-val 10 'other-val 20})
;;=> nil
user> some-val
;;=> 10
user> other-val
;;=> 20
notice that this function interns values in the namespace it gets called from, thanks to *ns* dynamic var:
user> (ns a2)
a2> (user/intern-vals {'some-val "asd" 'other-val "xxx"})
;;=> nil
a2> some-val
;;=> "asd"
a2> user/some-val
;;=> 10
Macros play with symbols. When you call your macro with "my-names", that symbol goes straight into the macro, there's no lookup of the var as there would be in a function call. Then the macro says (for... and instead of a sequence there's a symbol!
As for what you should do... well, you may use resolve inside the macro, but then the macro will ONLY work if given a symbol.
Addressing #Alan Thompson's question "[...] why [do] you want to do
this?": I have a specification (a deeply nested map) of "resources"
and it would be rather handy to have a macro generate defs (records)
for these resources in order to use them down the line. So I guess no
reason out of the ordinary "It would DRY up things". :) At this time I
found a way by wrapping my-names in an eval. The question that remains
is: Is this idiomatic, or is there a better way?
I would make a single var that contains them all. And allow the macro caller to specify the var name they want. Thus making the macro "hygenic".
Is there any particular reason they have to be root vars?
The reason it doesn't work is the macro is passed the symbol my-list, not its value. So yes, you can eval it to find its value.
Considering that you are doing a (def my-list ...) first, why not make that the very def that declares your processed data struct? For example:
(def my-processed-set
(my-processing-macro '(a b c)))
or combining it
(defresources my-processed-resources '(a b c))
where defresources is your macro, and it binds the resultant set to the var referenced by the symbol passed in my-processed-resources
Then use them like (:resource-1 my-processed-resources)
Which brings you all the way back to just using a function.
(def my-processed-set
(my-processing-function '(a b c)))
data > functions > macros.
What I am trying to achieve is to implement an abstract class with reify inside a macro, but the expressions that should return on expansion time would be supplied to the macro quoted:
(defmacro a-printable
[body]
`(reify Printable
(print [this g# pf# page#]
(if (= page# 0)
(do
~body ;; the supplied part
(Printable/PAGE_EXISTS))
(Printable/NO_SUCH_PAGE)))
(def exp '(do (.translate g (.getImageableX pf) (.getImageableY pf))
(.drawString g "foo" 10 10))) ;; the form to pass
(a-printable exp)
The proplem is that in the expression I pass, I want to use the automatic generated vars defined inside the macro and inside reify g#, pf#.
I tried to add the quoted expression with (ns-resolve *ns* g) (ns-resolve *ns* pf) but with no lack, I am not sure that is being resolved inside the macro.
The g is java.awt.Graphics which is an abstract class and the pf is java.awt.print.PageFormat, which is normal class with constructor.
Does anyone has any idea how to achieve that, or turn me to the correct direction?
I believe the trick is that if you don't want namespaced symbols in a macro, you can prefix them with ~', e.g. ~'g. Then I did the following other modifications to your macro:
Prefix the body parameter with & to make it variable length.
Splice the body into the macro
Remove the parenthesis around (Printable/PAGE_EXISTS) and (Printable/NO_SUCH_PAGE): Those are static variable values that you want to return, not function calls.
This is what the fixed macro looks like:
(defmacro a-printable
[& body]
`(reify Printable
(print [~'this ~'g ~'pf ~'page]
(if (= ~'page 0)
(do
~#body ;; Splice it!
Printable/PAGE_EXISTS)
Printable/NO_SUCH_PAGE))))
And this is how you create an instance. Note that I do not need to wrap the argument to the macro with do:
(def p (a-printable
(.translate g (.getImageableX pf) (.getImageableY pf))
(.drawString g "foo" 10 10)))
A note however: I am not sure it is a good practice to introduce new symbols, such as pf and g, but I cannot find the reference mentioning why that would be a bad practice. There are ways of achieving similar things to what is being asked in this question without resorting to macros. The version that does not use macros is not much longer:
(defn a-printable-fn [body-fn]
(reify Printable
(print [this g pf page]
(if (= ~'page 0)
(do
(body-fn this g pf page)
Printable/PAGE_EXISTS)
Printable/NO_SUCH_PAGE))))
(def p (a-printable-fn
(fn [this g pf page]
(.translate g (.getImageableX pf) (.getImageableY pf))
(.drawString g "foo" 10 10))))
My question is: how can I get the args list and expressions of a received function ?
I'm trying to do something like this:
(defn first-fn [[args exprs]]
(println "Args:" args)
(println "Exprs:" exprs))
(first-fn (fn [a b c] (println "something")))
So, first-fn would print:
Args: [a b c]
Exprs: (println "something")
My goal is to create a macro that can use the args list of the received function.
Thank you.
Edit:
Use case:
I'm using compojure https://github.com/weavejester/compojure
You can define routes like this:
(GET "/:id" [id] (body_here id))
But I would like to change the syntax to be:
(defn handler-fn [id] (body_here id))
...
(GET "/:id" handler-fn)
So the handler (body) can be extracted from the routes, and might be reused as well.
I tried to reuse compile-route https://github.com/weavejester/compojure/blob/master/src/compojure/core.clj#L172
(defmacro MY_GET [path fn-src]
(let [fn-fn (second fn-src)
arg-vec (nth fn-src 2)
forms (drop 3 fn-src)]
(compojure.core/compile-route :get path arg-vec forms)))
But when I call:
(MY_GET "/:id" handler-fn)
It says: Don't know how to create ISeq from: clojure.lang.Symbol
You cannot do this with functions, you directly need a macro to do this and even then it is not straight-forward. First, let's explain the difference: macros are basically evaluated at compile-time and the result of this evaluation is then evaluated at run-time. The interesting part is that the evaluation at compile-time gets the literal, unevaluated arguments to the macro as data and not, like normal functions would, the evaluated arguments at run-time. So, your approach cannot work, because at the time first-fn receives it's arguments (at run-time), they are already evaluated -- in your example, first-fn receives nil as arguments. Cf. the documentation at clojure-doc for a much better explanation.
Now, solving your request with a macro requires the macro to parse the arguments (remember: at compile time, code is data) that it receives -- i.e. in your example, it needs to parse the sequence (fn [a b c] (println "something")) that builds up the function call you hand over to it. Probably you would want to cover other cases besides the fn one (e.g. the # short-hand), that's what it makes the problem not straight-forward in the general case.
This parsing could in the end be handled by a normal function parsing, e.g. a sequence. So, try solving a different puzzle first: build a function parse-code-sequence that takes a sequence (that looks like the functions you would hand over) and returns the args and expr -- note the quote (') in front of fn.
user> (parse-code-sequence '(fn [a b c] (println "something")))
{args: [a b c], expr: (println "something")}
Some hints to this: in the example here, which is showing the most used case, the sequence just consists of three elements and you don't need the first one. But the general case is a little bit more complex, cf. the official documentation on fn.
A final remark: when you implement the macro, you need to think about what it resolves to -- just adding the print-statements is easy, but do you also want to evaluate the arguments normally (so your macro becomes something like a debugging aid) or do you want to do something else?
Update to reflect your use-case
Your MY-GET macro is not doing what you think it's doing.
Take a look at the arguments that the macro gets: why do you think it can magically retrieve the function definition of handler-fn, when all that you give as argument to MY_GET is the symbol/var handler-fn? You would need to retrieve the source, but this usually will not be possible (cf. this SO question on retrieving the source of a function definition).
You are also missing a backquote before the call to compile-route: you want the call to compile-route to happen at run-time, not at compile time. Currently, the result of the macro evaluation is the result of the call to compile-route (at compile-time). Take a look at macroexpand which would show you the result of the macro-expansion. Basically, you want the macro to return the call to compile-route.
I don't see any easy way that you could accomplish what you look for. The argument vector of a route definition is defining what needs to be handed over. Even if you extract that to a function definition, compojure still needs to know what to hand over to that function.
Here is an example of what you could do.
(ns xyz
(:require
[tupelo.core :as t]
))
(t/refer-tupelo)
(spyx *clojure-version*)
(defmacro dissect [ fn-src ]
(let [fn-fn (first fn-src)
arg-vec (second fn-src)
forms (drop 2 fn-src) ]
(spyx fn-fn)
(spyx arg-vec)
(spyx forms)
; Here is the return value; ie the transformed code
`(defn my-fn
~arg-vec
(apply + ~arg-vec))))
; show the result
(newline)
(println
(macroexpand-1
'(dissect
(fn [a b c]
(println "the answer is")
42))))
; call it for real
(newline)
(dissect
(fn [a b c]
(println "the answer is")
42))
; use the generated function
(newline)
(spyx (my-fn 1 2 3))
with result:
*clojure-version* => {:major 1, :minor 8, :incremental 0, :qualifier nil}
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(clojure.core/defn tst.clj.core/my-fn [a b c] (clojure.core/apply clojure.core/+ [a b c]))
fn-fn => fn
arg-vec => [a b c]
forms => ((println "the answer is") 42)
(my-fn 1 2 3) => 6
Your project.clj needs the following to make spyx work:
:dependencies [
[tupelo "0.9.11"]
The following code is from chapter 8.1.1 of (the second edition of) The Joy of Clojure by Fogus, Houser:
(defn contextual-eval [ctx expr]
(eval
`(let [~#(mapcat (fn [[k v]] [k `'~v]) ctx)] ; Build let bindings at compile time
~expr)))
(contextual-eval '{a 1, b 2} '(+ a b))
;;=> 3
(contextual-eval '{a 1, b 2} '(let [b 1000] (+ a b)))
;;=> 1001
I do not really understand the meaning of the construction `'~v. Can somebody please elaborate on that?
In the book, it is only said that
The bindings created use the interesting `'~v pattern to garner the value of the built
bindings at runtime.
For example
(contextual-eval '{a 1, b 2} '(+ a b))
is expanded to
(let [a '1 b '2] (+ a b)))
and I don't understand why those quotes are introduced, what they are good for.
Also, we have the following behaviour:
(contextual-eval '{a 1, b (+ a 1)} '(+ a b))
ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number
(defn contextual-eval' [ctx expr]
(eval
`(let [~#(mapcat (fn [[k v]] [k v]) ctx)]
~expr)))
(contextual-eval' '{a 1, b (+ a 1)} '(+ a b))
;=> 3
That expression uses almost all of the special line-noise-looking symbols available in Clojure, so it's worth picking it apart:
` is a reader-macro for "syntax-quote"
"syntax-quote" is special among the reader macros because you can only call that function via it's short form. You can't for instance call something like (syntax-quote something-here ) instead you would write `something-here. It provides a rich set of options for specifying what parts of the expression after it should be evaluated and which should be taken literally.
'Is a reader-macro shortcut for the quote special form. It causes the expression that it wraps not to be evaluated, and instead to be treated as data. If you wanted to write a literal quote form without evaluating it, you could write `'something to get `(quote something) as the result. And this would cause the resulting quote expression not to be evaluated, just returned as is without running it yet.
~ is a part of the syntax of syntax-quote (it's "quote" with a syntax) that means "actually let this part run" so if you have a big list that you want taken literally (not run right now), except you have one item that you really do want evaluated right now, then you could write `(a b c ~d e f g) and d would be the only thing in that list that gets evaluated to whatever it's currently defined to be.
So now we can put it all together:
`'~ means "make a quote expression that contains the value of v as it is right now"
user> (def v 4)
#'user/v
user> `'~v
(quote 4)
And on to the motivation for this fancyness:
(contextual-eval '{a 1, b 2} '(+ a b))
seems like just adding some extra thinking without any benefit because it's basically just quoting the values 1 and 2. Since these are proper "values" they never change anyway.
Now if the expression was instead:
(contextual-eval
'{a (slurp "https://example.com/launch?getCode")
b the-big-red-button}
'(press b a))
Then it would make more sense to be careful about when that particular bit of code runs. So this pattern is about controlling which phase of a programs life actually runs the code. Clojure has several "times" when code can run:
at macro-evaluation time: while the code is being formed. (side effects here require much forethought).
when your namespaces are loading: this is when forms at the top level run. This often happens when you start you program and before main is invoked.
things that run as a result of running main
ps: the above definitions are tailored to the context of this question and not intended to use the "official" terms.
I want to define a macro that randomly chooses one of the given expressions and evaluates it. For example,
(equal-chance
(println "1")
(println "2"))
should print "1" half the time and "2" the other half.
I tried using,
(defmacro equal-chance
[& exprs]
`(rand-nth '~exprs))
but this returns one of the quoted forms, rather than evaluating it (i.e. it would return (println "1") rather than actually printing "1"). I cannot use eval because it does not preserve the bindings. For example,
(let [x 10] (eval '(println x)))
complains that it is unable to resolve symbol x.
Is there a way to evaluate a quoted form in the local scope? Or maybe there is a better way to go about this?
You can't evaluate a run-time value in a lexical environment that only exists at compile time. The solution is to use fn instead of quote and a function call instead of eval:
(defmacro equal-chance [& exprs]
`((rand-nth [~#(map (fn [e] `(fn [] ~e)) exprs)])))
The way this works is by expanding (equal-chance e1 e2 ...) into ((rand-nth [(fn [] e1) (fn [] e2) ...])).
Just don't quote the call to rand-nth or the expressions. This will do what you want:
(defmacro equal-chance
[& exprs]
(rand-nth exprs))