I'm trying to write a macro to require some namespaces programmatically, so that the result for a passed-in argument would be (require 'value-of-argument). If I say (defmacro r [x] `(require ~x)) then I get (require value-of-x) as expected, but I can't work out how to get the quote in there.
Edit: here's a simpler example of my problem:
(defmacro q [x] `(str ~x))
=> (map (fn [e] (q e)) (map symbol ["x" "y" "z"]))
=> ("x" "y" "z")
however,
(defmacro q [x] `(str '~x))
=> (map (fn [e] (q e)) (map symbol ["x" "y" "z"]))
=> ("e" "e" "e")
All you need is to quote the argument again, like this:
(defmacro r [x] `(require '~x))
It should do the trick.
EDIT: The above won't work since x isn't known at compile time, when the macro is expanded.
However, now that I think about it, why not just call require directly, without a macro?
This seems to work:
(require (symbol "clojure.walk"))
Does that help?
(defmacro r [x] `(require (quote ~x)))
Related
This macro returns the values of the "magic" &env as a map, so that
(let [xyz "ZYX"] (get-env)) returns {xyz "ZYX"}, where the key is a Symbol.
(defmacro get-env []
(let [ks (keys &env)]
`(zipmap '~ks [~#ks])))
The expression '~ks evaluates the ks into Symbols at the macro-expansion phase (right?), but then quotes the expansion, so that the Symbols don't get evaluated into their values ("ZYX" in our example), but rather stay as Symbols (xyz). Is that right?
About [~#ks]: It evaluates ks into an seq of Symbols at the macro-expansion phase (right?) (and splices them and forms a vector with []). But how does that allow these Symbols to get further evaluated into their values ("ZYX" in our example) -- is there a second evaluation step, applied immediately after the first?
Another variant is
(defmacro local-env [] (->> (keys &env)
(map (fn [k] [(list 'quote k) k])) (into {})))
Your macro takes all the keys from the env. Then it uses the keys (a
list of symbols) to zip both the list of keys with spliced symbols
inside a vector. So what you get from
(let [x 42]
(get-env))
is
(let [x 42]
(zipmap '(x) [x]))
This is a compile-time transformation of your code (the whole point of
macros). The resulting code at runtime will use the 42 from the bound
x.
Preface
You may also be interested in the book "Clojure Macros", and in this StackOverflow question:
How do I write a Clojure threading macro?
Discussion
When in doubt, ask the compiler. Consider this code using my favorite template project:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defmacro getenv []
(prn :env &env)
(prn :env-meta (meta &env))
(prn :form &form)
(prn :form-meta (meta &form)))
(defn go []
(newline)
(prn :01)
(getenv)
(let [x 1
y "yyy"]
(newline)
(prn :02)
(getenv))
)
(dotest
(go))
with output
:env nil
:env-meta nil
:form (getenv)
:form-meta {:line 15, :column 3}
:env {x #object[clojure.lang.Compiler$LocalBinding 0x1ab07559 "clojure.lang.Compiler$LocalBinding#1ab07559"], y #object[clojure.lang.Compiler$LocalBinding 0x26c79134 "clojure.lang.Compiler$LocalBinding#26c79134"]}
:env-meta nil
:form (getenv)
:form-meta {:line 21, :column 5}
Testing tst.demo.core
:01
:02
so we can see the 4 (prn ...) outputs for each call to getenv. In the case where there are no local bindings, we get
&env ;=> nil
and for the case with the let we get a map like
(let [env-val (quote
{x :compiler-local-1
y :compiler-local-1})
ks (keys env-val)
ks-vec [ks]
]
(spyx env-val)
(spyx ks)
(spyx ks-vec)
)
with result
env-val => {x :compiler-local-1, y :compiler-local-1}
ks => (x y)
ks-vec => [(x y)]
At this point, I'm not quite sure what your desired result is. Could you modify the question to add that?
BTW, there is no hidden 2nd step, if I understand your question correctly.
Also
I rewrite your local-env and got the following result:
(defmacro local-env []
(prn :into-result
(into {}
(mapv
(fn [k] [(list 'quote k) k])
(keys &env)))))
(let [x 1
y "yyy"]
(newline)
(prn :03)
(local-env))
with result
:into-result {(quote x) x,
(quote y) y}
so I think there is some confusion here.
I was doing a small project in clojure and I wonder if there is something like this:
(let [myvar "hello"] (println (read-var "myvar")))
where the "read-var" function finds that there is a variable with the name passed as string and returns it's value.
I found this load-string function but it seems that doesn't work with let bindings.
Thank you!
I would say that in case you're in need of this kind of behaviour, you're probably not doing it right. in fact i can't even imagine why would someone want to do this in practice
but there is a way
clojure macros have special implicit parameter, called &env, allowing you to get local bindings. So you could use this feature for local vars resolution at runtime:
(defmacro get-env []
(into {} (map (juxt str identity)) (keys &env)))
notice that this macro doesn't require to know your desired var name at compile time, it rather just lifts the bindings from macro scope to runtime scope:
(let [x 10]
(let [y 20]
(get-env)))
;;=> {"x" 10, "y" 20}
(let [a 10
b 20
c 30
env (get-env)]
[a b c env])
;;=> [10 20 30 {"a" 10, "b" 20, "c" 30}]
even this
(let [a 10
b 20
c 30
env (get-env)]
(get-env))
;;=> {"a" 10, "b" 20, "c" 30, "env" {"a" 10, "b" 20, "c" 30}}
(let [x 10] (println ((get-env) "x")))
;;=> 10
;; nil
so the behaviour is dynamic, which could be shown with this fun example:
(defn guess-my-bindings [guesses]
(let [a 10
b 20
c 30]
(mapv #((get-env) % ::bad-luck!) guesses)))
user> (guess-my-bindings ["a" "zee" "c"])
;;=> [10 :user/bad-luck! 30]
but notice that this get-env effect is limited to the bindings effective at it's expand-time. e.g:
(let [x 10
y 20
f (fn [] (let [z 30]
(get-env)))]
(f))
;;=> {"x" 10, "y" 20, "z" 30} ;; ok
(def f (let [x 10
y 20]
(fn [] (let [z 30]
(get-env)))))
(f)
;;=> {"x" 10, "y" 20, "z" 30} ;; ok
but
(let [qwe 999]
(f))
;;=> {"x" 10, "y" 20, "z" 30} ;; oops: no qwe binding
I am not aware of some approach to accomplish this if read-var has to be a function. If read-var were a macro and its argument is a string literal it would be possible to implement read-var so that the code that you wrote works. Another approach would be to build a macro read-var that uses eval, but that would also not be possible because eval cannot access local bindings as explained in this answer.
The closest I could come up with that (i) implements read-var as a function and (ii) lets you pass runtime values as arguments to read-var is the following:
(def ^:dynamic context {})
(defmacro with-readable-vars [symbols & body]
`(binding [context (merge context ~(zipmap (map str symbols) symbols))]
~#body))
(defn read-var [varname]
(get context varname))
and you can now use this code like
(let [myvar "hello"]
(with-readable-vars [myvar]
(println (read-var "myvar")))) ;; Prints hello
The difference compared to your code is that you have to declare the variables that should be readable using the with-readable-vars macro. Obviously, you can build another macro that combines let and with-readable-vars if you like:
(defmacro readable-let [bindings & body]
`(let ~bindings
(with-readable-vars ~(vec (take-nth 2 bindings))
~#body)))
(readable-let [myvar "hello"]
(println (read-var "myvar")))
The above code assumes you are not using advanced features such as destructuring for your bindings.
Pretty simple here:
(let [myvar "hello"]
(println myvar))
;=> hello
Please see this sample project, esp. the list of documentation.
If you really want to pass the name of the variable as a string, you will
need the eval function:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[schema.core :as s]
))
(dotest
(let [mydata "hello"]
(is= "hello" mydata) ; works
))
(def myVar "hello") ; creates a Clojure 'Var': tst.demo.coore/myVar
(dotest
; will fail if don't use fully-qualified namespace in string
(let [parsed (clojure.edn/read-string "(println :with-eval tst.demo.core/myVar)")]
(eval parsed)
;=> `:with-eval hello`
))
(let [a (clojure.core.async/chan)]
(case a
a :foo
:bar))
#=> :bar
I would expect :foo here. What am I doing wrong?
On the other hand (condp = chan ...) does the job.
PS:
Basically I am trying to do following thing:
(require '[clojure.core.async :as a])
(let [chan1 (a/chan 10)
chan2 (a/chan 10)]
(a/>!! chan1 true)
(let [[v c] (a/alts!! [chan1 chan2])]
(case c
chan1 :chan1
chan2 :chan2
:niether)))
#=> :neither
The docs for case have the answer
The test-constants are not evaluated. They must be compile-time
literals, and need not be quoted.
The correct solution is to use cond:
(let [chan1 (ca/chan 10)
chan2 (ca/chan 10)]
(ca/>!! chan1 true)
(let [[v c] (ca/alts!! [chan1 chan2])]
(spyx (cond
(= c chan1) :chan1
(= c chan2) :chan2
:else :neither))))
;=> :chan1
Case uses unevaluated test-constants for the left-hand-side of the clause. Plain symbols, like chan1 here will match only the symbol with the same name, not the value of the local binding with that name; chan1 will match 'chan1
(Apologies if this is a duplicate of another question, my search for all those fancy special characters didn't yield anything.)
I'm reading Mastering Clojure Macros and have trouble understanding the following example:
(defmacro inspect-caller-locals []
(->> (keys &env)
(map (fn [k] [`'~k k]))
(into {})))
=> #'user/inspect-caller-locals
(let [foo "bar" baz "quux"]
(inspect-caller-locals))
=> {foo "bar", baz "quux"}
What is the difference between the following and the much simpler 'k?
`'~k
As far as I understand, the innermost unquote ~ should simply reverts the effect of the outermost syntax-quote `, but a short experiment reveals that there's more to it:
(defmacro inspect-caller-locals-simple []
(->> (keys &env)
(map (fn [k] ['k k]))
(into {})))
=> #'user/inspect-caller-locals-simple
(let [foo "bar" baz "quux"]
(inspect-caller-locals-simple))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: k in this context, compiling:(/tmp/form-init4400591386630133028.clj:2:3)
Unfortunately, my usual investigation approach doesn't apply here:
(macroexpand '(let [foo "bar" baz "quux"]
(inspect-caller-locals)))
=> (let* [foo "bar" baz "quux"] (inspect-caller-locals))
(let [foo "bar" baz "quux"]
(macroexpand '(inspect-caller-locals)))
=> {}
What am I missing here?
Let's first establish what the k inside the macro is:
(defmacro inspect-caller-locals []
(mapv (fn [k]
(println (class k)))
(keys &env))
nil)
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; clojure.lang.Symbol
So you each k inside the function is a symbol. If you return a symbol from a macro (ie generate code from it), clojure will lookup the value that it refers to and print it. For instance you could do this:
(defmacro inspect-caller-locals []
(mapv (fn [k]
[(quote x) k]) ;; not the "hard coded" x
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; [[1 1]]
What you want however is the actual symbol. The problem (as you noted) is that quote is a special form that DOES NOT EVALUTE whatever you pass it. Ie, the k will not obtain the function parameter but stay k which is not usually defined:
(defmacro inspect-caller-locals []
(mapv (fn [k]
[(quote k) k])
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; => Error
(let [k 1]
(inspect-caller-locals))
;; Prints:
;; [[1 1]]
You somehow need to evaluate what you pass into quote, this is not however possible since that isn't what quote does. Other functions, such as str don't have that problem:
(defmacro inspect-caller-locals []
(mapv (fn [k]
[(str k) k])
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; [["x" 1]]
The trick is to go one level deeper and quote the quote itself so you can pass the symbol to it:
(defmacro inspect-caller-locals []
(mapv (fn [k]
[;; This will evaluate k first but then generate code that
;; wraps that symbol with a quote:
(list (quote quote) k)
;; Or equivalently and maybe easier to understand:
(list 'quote k)
k])
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; [[x x 1]]
Or by using the reader that can do this for you:
(defmacro inspect-caller-locals []
(mapv (fn [k]
[`(quote ~k)
`'~k
k])
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; [[x x 1]]
Because after all:
(read-string "`'~k")
=> (clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list k)))
(defmacro inspect-caller-locals []
(mapv (fn [k]
[(clojure.core/seq (clojure.core/concat (clojure.core/list (quote quote)) (clojure.core/list k)))
k])
(keys &env)))
(let [x 1]
(inspect-caller-locals))
;; Prints:
;; [[x 1]]
Some alternative, and equivalent, ways of writing
`'~k
are:
`(quote ~k) ;; expands the ' reader macro to the quote special form
(list 'quote k) ;; avoids syntax quote entirely
You are pretty much right to think that
the innermost unquote ~ should simply reverts the effect of the outermost syntax-quote
The only thing missing from your description there is that you can't pull quote outside of a syntax-quoted expression, since quote is a special form and changes the meaning of what's inside. Otherwise,
'`~k
would be equivalent to 'k - and as you noticed, it's not!
I'll echo #amalloy's general advice, that trying syntax-quoted stuff in the REPL, outside of the context of macros/macroexpansion, is the best way to get your head around these things.
p.s. Also, I'll make a note that I need to fix this confusion by explaining better in a future book edition ;)
Given that I have a expression of the form
'(map? %)
How do I convert it into something like
'#(map? %)
So that I can ultimately expand it into something like
'(apply #(map? %) value)
I think I should use a macro in some way, but am not sure how.
The # invokes a reader macro and reader macros expansion happen before normal macros expansion happens. So to do what you have mentioned, you need to go through the reader in your macro using read-string as shown below.
(defmacro pred [p v]
(let [s# (str \# (last p))]
`(apply ~(read-string s#) ~v)))
user=> (pred '(map? %) [{}])
true
user=> (pred '(map? %) [[]])
false
In case the data i.e the predicate expression is available at runtime then you need to use a function (which is more flexible then macro).
(defn pred [p v]
(let [s (read-string (str \# p))]
(eval `(apply ~s ~v))))
user=> (map #(pred % [12]) ['(map? %)'(even? %)])
(false true)
#(...) is a reader macro. I don't think that you can generate expression with reader macro. For example '#(map? %) will automatically expand into (fn* [p1__352#] (map? p1__352#)) or something similar.
Here's a somewhat relevant discussion on other reader macro.
Would it be possible to change format of the predicate? If it looked something like:
'([arg1] (map? arg1))
Then it would be trivial to make a function form it:
(cons 'fn '([arg1] (map? arg1)))
(def pred (eval (cons 'fn '([p](map? p)))))
#'predicate.core/pred
(pred {})
true
(pred 10)
false
Now please don't hate me for what I'm going to post next. I wrote an overly simplified version of the function reader macro:
(defn get-args [p]
(filter #(.matches (str %) "%\\d*")
(flatten p)))
(defn gen-args [p]
(into []
(into (sorted-set)
(get-args p))))
(defmacro simulate-reader [p]
(let [arglist (gen-args p)
p (if (= (str (first p)) "quote")
(second p)
p)]
(list 'fn (gen-args p) p)))
Using it is very straight-forward:
((simulate-reader '(map? %)) {}) ; -> true
; or without quote
((simulate-reader (map? %)) {})
; This also works:
((simulate-reader '(+ %1 %2)) 10 5) ; -> 15
The difference with the other solution given by #Ankur is:
I like mine less. I just thought it was a fun thing to do.
Does not require conversion to string and then applying reader macro to it.