I am new to Clojure and I am having trouble understanding its quoting system. I am writing a macro and I have made two similar cases - one works, the other doesn't. In a sense, I am just trying to surround my statement with try/catch condition.
Here's the code that works:
(defmacro safe
[arg1 arg2]
(list 'let arg1 arg2)
)
Here's the code that doesn't work
(defmacro safe
[arg1 arg2]
'(try
~(list 'let arg1 arg2)
(catch Exception e (str "Error: " (.getMessage e)))
)
)
after the ~ symbol, it should escape the quotes, but for some reason, it seems like it doesn't. The Error is: "Unable to resolve symbol: arg1 in this context...".
Thanks for your help!
Edit:
Code that I call the macro with:
(println (safe [s (new FileReader (new File "text.txt"))] (.read s)))
Also, I import this:
(import java.io.FileReader java.io.File)
Goal is to read first symbol from a file, while being safe from incorrect text file names. This is my school assignment by the way, so I should not use any other way to do it and the macro has to be called like that, I know about the with-open, etc.
Escaping (~) only works with quasi-quote (also known as syntax quote). You need to use the "back-quote" (`, found on the same key as ~ on most US keyboards), not the normal single-quote (', which is on the same key as "). It's a subtle difference graphically, which can be easy to miss.
You can also get rid of the list by not quoting the let and un-quoting arg1 and arg2. With these changes, we get something like this:
`(try ;; note back-quote, not regular quote.
(let ~arg1 ~arg2) ;; Getting rid of list — not necessary, but most find this more readable
(catch Exception e (str "Error: " (.getMessage e))))
Now, if we check our progress using macroexpand:
(macroexpand '(safe2 [s (new FileReader (new File "text.txt"))] (.read s)))
We get the following:
(try (clojure.core/let [s (new FileReader (new File text.txt))]
(.read s))
(catch java.lang.Exception user/e
(clojure.core/str Error: (.getMessage user/e))))
You might notice that in Clojure, quasi-quoted symbols are resolved when the macro is compiled. Symbols that can't be resolved are qualified with the current namespace (user in this case). The rationale for this is that it helps you write "hygienic" macros. In this case, however, we don't want to resolve the e symbol, because local variables can't be given a qualified name.
We have a few options now. The first is to basically forgo hygiene. That works in this case, since you aren't expanding any user-provided code inside the catch block. So there isn't any way the name e could conflict with a user variable. This solution would look like this:
`(try
(let ~arg1 ~arg2)
(catch Exception ~'e (str "Error: " (.getMessage ~'e))))
Note the use of ~'e instead of just e. The ~ is to escape from the quasi-quote, then we use a regular quote to quote e. It looks a little weird, but it works.
While the above solution works, it's arguably better to use a generated symbol instead of e. That way, if you ever change your macro to accept user-provided code for the catch block, you can be sure it will still work. In this particular case, an "auto-generated" symbol fits the bill perfectly. This looks as follows:
`(try
(let ~arg1 ~arg2)
(catch Exception e# (str "Error: " (.getMessage e#))))
Basically, whenever the Clojure reader encounters a symbol with a trailing # inside a quasi-quote form, it will produce a new gensym'd symbol and replace every occurence of that symbol (i.e., e#) with the gensym'd one. If we macroexpand this, we would get something like:
(try (clojure.core/let [s (new FileReader (new File text.txt))]
(.read s))
(catch java.lang.Exception e__66__auto__
(clojure.core/str Error: (.getMessage e__66__auto__))))
As you can see, every occurence of e# was replaced with a machine-generated symbol. Here e__66__auto__ is the auto-gen'd symbol.
Finally, while auto-gen is convenient, it is not always sufficient. The main issue is that, since auto-gen'd symbols are produced at read time, every evaluation of the quasi-quote form (i.e., expansion of the macro) will use the same auto-gen'd symbols. In this paricular case, that's fine. However, there are cases where this can lead to conflicts if, for example, nested macro forms are used. In these cases, it is necessary to use an explicitly gensym'd symbol every time the macro is expanded. With this approach, the body of your macro would look like this:
(let [e (gensym)]
`(try
(let ~arg1 ~arg2)
(catch Exception ~e (str "Error: " (.getMessage ~e)))))
Here e is a local variable in your macro, whose value is the a fresh symbol (via gensym). In the quasi-quote, we must escape e in order to use that gensym'd value.
If we expand this, we would get something like:
(try (clojure.core/let [s (new FileReader (new File text.txt))]
(.read s))
(catch java.lang.Exception G__771
(clojure.core/str Error: (.getMessage G__771))))
If we expand this again, we would find G__771 replaced by a different symbol (perhaps G__774). In contrast, the auto-gen'd solution (e#) would always use the same symbol for every expansion (at least until we recompile the macro).
Hopefully this gives you a better understanding of macros, symbols, and hygiene. Let me know if anything isn't clear.
There’s two problems here:
First, unsplicing (~ and ~#) work only inside syntax-quote (`). Syntax quote is usually chosen for macros because it also does symbol namespace resolution at macro definition place. Simple quote (') will keep symbols intact, so ns resolution will happen at macro invocation site. Because you do not control where and how your macro will be called, it might be very confusing.
Second, you cannot just declare new symbols in quoted code, it may cause name conflict with code around macro. Each new symbol introduced by macro should use suffix # so Clojure macroexpansion will replace it with new autogenerated name that cannot cause any name conflicts with user code.
(defmacro m []
`(let [x# 1]
x#))
(macroexpand-1 '(m)) =>
=> (clojure.core/let [x__6257__auto__ 1]
x__6257__auto__)
Note how let became fully-qualified clojure.core/let (avoiding ns resolution nuances later), and x# got replaced with x__6257__auto__ (avoiding name conflicts).
Your code should be written as this:
(defmacro safe [arg1 arg2]
`(try
(let ~arg1 ~arg2)
(catch Exception e#
(str "Error: " (.getMessage e#)))))
Check like this:
(macroexpand-1 '(safe [s (new FileReader (new File "text.txt"))] (.read s)))
↓↓↓
(try
(clojure.core/let [s (new FileReader (new File "text.txt"))]
(.read s))
(catch java.lang.Exception e__6283__auto__
(clojure.core/str "Error: " (.getMessage e__6283__auto__))))
I would also recommend using idiomatic names for macro args and make second argument of arbitrary length:
(defmacro safe-let [bindings & body]
`(try
(let ~bindings
~#body)
(catch Exception e#
(str "Error: " (.getMessage e#)))))
Related
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
I am trying to define a macro like this:
(defmacro foo-macro
"[name] is the name of the function to define
[meta-data] is the map that defines the meta-data of the function"
[name meta-data]
(let [vals (gensym "label-")]
`(def ~name
^#~meta-data
(fn [~vals] (eval nil)))))
However, I am getting this compilation error:
Unhandled java.lang.IllegalArgumentException Metadata must be
Symbol,Keyword,String or Map
I think this is normal. However, I am not sure how I can make such a macro to work, or if it is even possible.
Note: I do not want to use with-meta. Read the revision of the resolution of this other question to understand why: Clojure: issues getting the symbol of a function in a looping context.
The only reason why I would use with-meta, is if you have a way to make it return a non-AFunc identifier.
I'm sorry, but you do want to use with-meta, you just want to use it within your macro rather than within the code you return.
The form that you get by reading ^{:foo :bar} (fn []) is approximately the same as what you get by evaluating (with-meta `(fn [])), so that's what you want to do in your macro: evaluate the with-meta call while generating code.
(defmacro foo-macro
"[name] is the name of the function to define
[meta-data] is the map that defines the meta-data of the function"
[name meta-data]
(let [vals (gensym "label-")]
`(def ~name
~(with-meta `(fn [~vals] (eval nil))
meta-data))))
Calling (foo-macro fn-name {:foo "bar"}) should give you what you want: a function with (meta fn-name) returning {:foo "bar"} which, when printed, comes out something like #<user$fn_name user$fn_name#35df85a1>.
UPDATE:
Thanks for all the help so far. This is my new code, which works. But not exactly as I would like it to.
I need it to return the java exception (don't ask me why).
Example:
(safe (/ 1 0))
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>
which is how I want it to be. But when I make use of the other clauses of code that handle bindings etc, I get the Clojure exception:
(seefe [f(FileReader.(File. "C:/txtf.txt"))](. f read))
FileNotFoundException C:\txtf.txt (The system cannot find the file specified) java.io.FileInputStream.open (:-2)
What can I do to prevent this, and show the Java exception instead?
(defmacro safe [bindings & code]
(if (list? bindings)
`(try
(println "line 8: try")
~bindings
(catch Throwable e# e#))
(if (= (count bindings) 0)
`(try ~code
(catch Throwable e# e#))
`(let ~(subvec bindings 0 2)
(try
(safe ~(subvec bindings 2) ~#code)
(catch Throwable e# e#)
(finally
(. ~(bindings 0) close)))))))
OLD
I'm trying to get an assignment done, but it's impossible without any tutoring. My teachers expect us to teach ourselves Clojure in 1 week and complete this assignment. Everyone in my class is stuck and we already hate the teacher, lmao.
Okay, so the macro is supposed to be able to try code, and return a result or an exception. It's supposed to be able to handle expressions like:
(def v (safe [s (FileReader. (File. "file.txt"))] (. s read)))
If the code opened any filestreams or whatnot, it should close them in a finally clause.
This is what I got so far - I realize it's not working.
(defmacro safe [& body]
`(try ~#body
(catch Throwable e# e#)
(finally
(if (. ~#body isInstance Closable) (. ~#body close)))))
The error I get is:
Unable to resolve symbol: s in this context,
compiling:(NO_SOURCE_PATH:1)
I got desperate so I tried alot of different stuff, I tried:
to edit the macro:
(defmacro safe [& body]
`(try ~#body
(catch Throwable e# e#)
(finally (if (. ~#body isInstance Closable) (. ~#body close)))))
then run:
(safe (. (java.io.FileReader. (java.io.File. "C:/Users/Dyallo.L/Dropbox/DVK11/PROP/Clojure/txt.txt")) read))
Which resulted in:
No such var: clooj.cemerick.pomegranate/Closable,
compiling:(NO_SOURCE_PATH:1)
Someone mentioned the macro WITH-OPEN but I guess that won't work well with my generic macro. The macro isn't meant for opening files, but if they are, it should definately close them.
So darn, won't you give me a hand Stackoverflow-geniuses?
Thanks in beforehand.
from the example it looks like the first form passed to the safe macro is a vector of name expression pairs [a (open-something "/foo/bar") b (oopen-something-else)] followed be some number of expressions that use these symbols. if I'm interpreting the assignment correctly the result would be quite similar to with-open and the various with-connection macros. it's worth double checking this assumption before submitting your results of course. It would be much more difficult if the macro was supposed to find closable values in its body with out at least some knowledge of its structure.
with-open macro does exactly the same thing.
If there is variable (can be more than one) bound to created object it wraps the body to try-catch block with finallyclosing.
(macroexpand-1 '(with-open [f (FileReader. (File. "1.txt"))]
(do this with f)
(do that with f)))
=>
(clojure.core/let
[f (FileReader. (File. "1.txt"))]
(try (clojure.core/with-open []
(do this with f)
(do that with f))
(finally (. f clojure.core/close))))
If there are no bindings then it just returns the body
(macroexpand-1 '(with-open []
(do this with f)
(do that with f)))
=>
(do (do this with f)
(do that with f))
Update. Arthur has already explained "exception" part of your question.
I am trying to write a macro that, given a form, defines all missing symbols as themselve.
By now I have the following:
(def ^:private missing-symbol-pattern #"Unable to resolve symbol: (.+) in this context")
(cl/defn ^:private missing-symbol [s]
(cl/when-let [[_ sym] (cl/re-find missing-symbol-pattern s)] sym))
(cl/defmacro try-eval [expr]
`(try (do (cl/println '~expr) (cl/eval '~expr))
(catch Exception e#
(cl/if-let [sym# (do (cl/println (.getMessage e#)) (missing-symbol (.getMessage e#)))]
(cl/eval `(do
(def ~(cl/symbol sym#) '~(cl/symbol sym#))
(cl/println ~sym#)
(try-eval ~'~expr)))
(cl/eval `(throw ~e#))))))
(cl here is an alias for clojure.core)
I know that there can be problems with side-effects, but this is not important here (although a solution without side-effect problems would be better)
If there is more than one missing (unresolvable) symbol, I get the following Exception:
java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: java.lang.ClassCastException: clojure.lang.Symbol cannot be cast to clojure.lang.IDeref, compiling:(shen/primitives.clj:517)
Has anyone an idea on what to change? Is there some "ready made" solution?
Cheers
Rather than using try/catch to find symbols that won't resolve, maybe you could do something using &env symbol available inside of defmacro. The keys of &env are the locally defined symbols.
(Article about &env and &form.)
Using a combination of resolve and (keys &env) you can isolate the symbols that aren't defined, then you can choose to def the ones that are (not (or (resolve sym) (contains? &env sym))
We do something akin to this in the Midje source code, to determine which symbols in a tabular test are already defined and which ones aren't:
[Note: in the below code locals is in fact (keys &env)]
(defn- headings-rows+values [table locals]
(letfn [(table-variable? [s]
(and (symbol? s)
(not (metaconstant-symbol? s))
(not (resolve s))
(not (contains? locals s))))]
(split-with table-variable? (remove-pipes+where table))))
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.