I'm trying to write a macro that I can use to call a function on a new thread and print the name of the function as well as the name of the thread after running it.
So far what I have is:
(defmacro start-threads [f]
'(let [t (Thread. 'f)]
(prn (str "running " 'f " on: " (.getName t)))))
which when I run:
(start-threads funcname)
outputs: "running f on: Thread-47". and I would like it to output: "running funcname on: Thread-47. When I try unquoting it it try's to evaluate the function. I know I haven't run .start on the thread here, but I should be able to add that in afterward. I'm sure a macro isnt completely necessary here, I'm mostly wondering out of curiosity as I am just starting to wrap my mind around how macros in clojure work.
Basically, what you want is syntax-quote rather than normal quote.
(defmacro start-threads [f]
`(let [t# (Thread. ~f)]
(prn (str "running " '~f " on: " (.getName t#)))))
~f in a syntax-quote interpolates the value of f, '~f quotes that value and the t# makes an auto-gensym so the variable name won't conflict with any surrounding names.
But as you correctly note, you really don't need a macro for this. It could easily be a function:
(defn start-threads [f]
(let [t (Thread. f)]
(prn (str "running " f " on: " (.getName t)))))
Related
I'm working through some clojure examples from braveclojure:
http://www.braveclojure.com/writing-macros/
Currently I am trying to execute this
(ns turtle (:use clojure.pprint))
(def criticisms {:good "good code:" :bad "bad code:"})
(defn criticize-code
[[critkey code]]
`(println (~critkey criticisms) (quote ~code)))
(defmacro code-critic
[code-evaluations]
`(do ~#(map criticize-code code-evaluations)))
(println "executed code critic")
(code-critic {:good (+ 1 1) :bad (1 + 1)})
(println "code critic expansion")
(pprint (macroexpand '(code-critic {:good (+ 1 1) :bad (1 + 1)})))
;why isn't this executing?
(println "criticize code expansion")
(criticize-code [:good '(+ 1 1)])
Basically, I can verify that criticize-code returns properly formatted code through println; but I cannot actually execute it...can someone please tell me what I'm doing wrong?
Thank you!
The function criticize-code is being invoked. The quasi-quote, `, in the body of the function is a reader macro for syntax-quote, which means the following println form, after its trip through the syntax-quote reader, will be returned as data structure rather than executed. The criticize-code function is semantically equivalent to
(defn criticize-code
[[critkey code]]
(list
'clojure.core/println
(list critkey 'turtle/criticisms)
(list 'quote code)))
If you want to treat the resulting data structure as code at the REPL, you can eval it directly.
turtle=> (criticize-code [:good '(+ 1 1)])
(clojure.core/println (:good turtle/criticisms) (quote (+ 1 1)))
turtle=> (eval (criticize-code [:good '(+ 1 1)]))
good code: (+ 1 1)
nil
So why would you want a function that works like this? As a helper to a macro, as here for code-critic. Macros deal with code-as-data-as-code. Therefore, if you stick in a helper function at the as-data stage, it will need to return its result as data. Otherwise, the code you want to be compiled is just executed at "compile" time with its return value (println returns nil) compiled instead.
actually i am trying to perfectly understand clojure and particularly symbols
(def a 1)
(type a)
;;=>java.lang.Long
(type 'a)
;;=>clojure.lang.Symbol
I know that type is a function so its arguments get evaluated first so i perfectly understand why the code above work this way .In the flowing code i decided to delay the evaluation using macro
(defmacro m-type [x] (type x))
(m-type a)
;;==>clojure.lang.Symbol
and i am fine with that but what i fail to uderstand is this:
(m-type 'a)
;;=>clojure.lang.Cons
why the type of 'a is a cons
the character ' is interpreted by the clojure reader as a reader-macro which expands to a list containing the symbol quote followed by whatever follows the ', so in your call to (m-type 'a) the 'a is expanding to:
user> (macroexpand-1 ''a)
(quote a)
then calling type on the list (quote a) which is a Cons.
This may be a bit more clear if we make the m-type macro print the arguments as it sees them while it is evaluating:
user> (defmacro m-type [x] (println "x is " x) (type x))
#'user/m-type
user> (m-type 'a)
x is (quote a)
clojure.lang.Cons
I wrote two functions like this, but as you see most part of them are identical, so I want to write a macro to simplify them.
I understand the simple macro examples in my textbook, but I don't know how to write my own.
Here's my code:
(defn load-dict
; "Load database from a txt file previous saved"
[fname]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(def vvv (clojure.string/split line #"\s"))
;(println (str "count of vvv is " (count vvv)))
(if (< 1 (count vvv))
(add- dict (gen-word (nth vvv 0) (nth vvv 2) (nth vvv 1))))
)))
(defn load-article
; "Load article from a txt file"
[fname]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(def vvv (clojure.string/split line #"\s"))
;(println (str "count of vvv is " (count vvv)))
(if (< 1 (count vvv))
(add- article vvv ))
)))
Should I write a macro like:
(defmacro load- [target fname &expr)
`(...
(add- ~target expr)))
I actually don't know how to write such a macro. I just hate duplicate code.
PS, the tow function work fine. I don't care about the variable this is part of the code.
I would use a let block instead of def. Using def will bind a var and define vvv in your namespace. A macro is not really needed. You could simplify your code like this:
(defn load-from
"Load database from a txt file previous saved"
[fname load-fn]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(let [vvv (clojure.string/split line #"\s")]
(when (< 1 (count vvv))
(load-fn vvv))))))
And invoke it like this
(load-from "myfile.txt" #(add- dict (apply gen-word (take 3 %))))
(load-from "myfile.txt" #(add- article %))
user1944838 is correct here in that you don't need a macro, and since macros make code ,which does not need them, slightly harder to work with in some contexts (you can't pass it to map or apply for instance), using functions is preferable in practice. Understanding how to write macros correctly is however very important.
I would write this as a template macro that binds a name you pass it to each word and then call the body you pass to the macro which would in turn use that word through the symbol name.
(defmacro with-loaded-article
[[name-for-line fname] & body]
`(with-open [rdr# (io/reader ~fname)]
(doseq [line# (line-seq rdr#)]
(println line#)
(let [~name-for-line (clojure.string/split line# #"\s")]
~#body))))
The [name-for-line fname] expression destructures the first argument into a single "binding form" that will be used to generate a symbol and the values that it will resolve to. This format is very common in "with-*" macros such as with-open except here I only take one binding form to keep the code smaller.
the rdr# and line# symbols inside the syntax-quote are a feature of syntax quote called "auto gensyms" that causes any symbol in side the syntax quote ending with # to be replaced by a unique, though consistent symbol in the resulting expression.
the ~# is the splicing-unquote feature of syntax-quotes that causes body in this case to be inserted without adding ( ) around it.
We can see how this expands with macroexpand-1 and pprint hint: (use 'clojure.pprint)
hello.core> (pprint (macroexpand-1
`(with-loaded-article [line "tmp.txt"] (println line))))
(clojure.core/with-open
[rdr__6337__auto__ (clojure.java.io/reader "tmp.txt")]
(clojure.core/doseq
[line__6338__auto__ (clojure.core/line-seq rdr__6337__auto__)]
(clojure.core/println line__6338__auto__)
(clojure.core/let
[hello.core/line (clojure.string/split line__6338__auto__ #"\s")]
(clojure.core/println hello.core/line))))
And when we run the resulting code we get the lines of the file as sequences:
hello.core> (with-loaded-article [line "tmp.txt"] (println line))
hello world
[hello world]
world hello
[world hello]
and internet hello as well
[and internet hello as well]
I'm working on some java library wrapper.
We have some object with methods and can invoke them with (.method object ...). I want to create more convenient api with number of functions (method object ...).
I wrote a simple macro:
(defmacro ^{:private true} gen-method [method & argv]
`(def ~method (memfn ~method ~#argv)))
Now I can invoke (gen-method charAt i) for example and after that I can use (charAt "string" 1).
But I have a lot of method to generate and want to use something like:
(map #(gen-method (-> %1 name symbol arg)) [:charAt :substring ...])
But this is not working. Next example is not working too:
(map #(let [fname (-> %1 name symbol)] (gen-method fname arg)) [:charAt :substring ...])
What I need to change in macro or add something for correct behaviour ?
Thanks.
You will have to take the map operation inside the macro itself as the map operation should happen at compile time i.e form expanding time and not at runtime.
Something like below:
(defmacro ^{:private true} gen-methods [methods]
`(do
~#(map (fn [[x & i]] (let [m (-> x name symbol)] `(def ~m (memfn ~m ~#i)) )) methods)))
(gen-methods [ [:charAt i] [:substring i]])
When applying a macro multiple times with a another macro, bare Symbols are not inserted into the current context:
(defmacro ty [type]
`(deftype ~type []))
(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg))
)
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
(derive ::Person ::Base)
(derive ::Animal ::Base)
(ty Me)
(prn ::Me)
(prn Me)
(empties Empty)
(prn ::Empty)
(prn Empty)
The last line gives: "Unable to resolve symbol: Empty in this context", even though when using the direct macro ty, it works. Any way to solve this? If possible without eval it would be much better.
(defmacro empties [& args]
(doseq [arg args]
`(ty ~arg)))
(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)
This is wrong. Your empties call means that the macro expansion function for empties gets as arguments the symbols Base, Person, and Animal. It then evaluates the ty macro call for each, but does not return anything, as doseq always returns nil. So, the expanded code from that empties call is nil. You need to return a single form from your macro function. You should wrap the multiple forms into a do, and actually return all the subforms to that:
(defmacro empties [& args]
`(do ~#(map (fn [arg]
`(ty ~arg))
args)))
FWIW, I prefer to write #Svante's solution as
(defmacro empties [& args]
(cons `do
(for [arg args]
`(ty ~arg))))
which is also pretty close to the structure of your doseq approach.