Can try and catch be in different (but nested) macros? - clojure

The try is in one macro, the catch in a second one that is called by the first. How to get the following to work?
(defmacro catch-me []
`(catch ~'Exception ~'ex
true))
(defmacro try-me []
`(try (+ 4 3)
(catch-me)))
Expanding try-me looks good:
(clojure.walk/macroexpand-all '(try-me))
yields
(try (clojure.core/+ 4 3) (catch Exception ex true))
but calling (try-me) yields:
"Unable to resolve symbol: catch in this context",
which, BTW, is also the message you would get in the REPL when using catch when not in a try.
UPDATE:
This is how I can get it to work (thanks, #Barmar), here you can see the actual context of my code:
(defmacro try-me [& body]
`(try
~#body
~#(for [[e msg] [[com.mongodb.MongoException$Network "Database unreachable."]
[com.mongodb.MongoException "Database problem."]
[Exception "Unknown error."]]]
`(catch ~e ~'ex
(common/site-layout
[:div {:id "errormessage"}
[:p ~msg]
[:p "Error is: " ~e]
[:p "Message is " ~'ex]])))))
but this is what I was hoping for (using a separate macro catch-me):
(defmacro try-me [& body]
`(try
~#body
(catch-me com.mongodb.MongoException$Network "Database unreachable.")
(catch-me com.mongodb.MongoException "Database problem.")
(catch-me Exception "Unknown error.")))
I think this would be easier to write / maintain.
Any ideas? I need syntax-quoting because I am passing parameters, that is why unfortunately Arthur's answer cannot be applied (or can it somehow?), but I didn't post my actual context until just now.

The reason you get that error is because the syntax for try is:
(try expr* catch-clause* finally-clause?)
This means that there can be any number of expr forms before the catch and finally clauses. try scans the exprs until it finds one that begins with catch or finally. It does this before expanding any macros, since it's just trying to figure out where the exprs and and the catch/finally clauses begin. It collects all the catch and finally clauses and establishes the appropriate error handling environment for them.
Once it does this, it executes all the expr forms normally. So it expands their macros, and then executes them. But catch is not a function or special form, it's just something that try looks for in the earlier step. So when it's executed normally, you get the same error as when you type it into the REPL.
What you should probably do is write a macro that you wrape around your entire code that expands into the try/catch expression that you want. Without an example of what you're trying to accomplish, it's hard to give a specific answer.

The short answer is YES, though nesting macros with special forms can lead to some double-quoting headaches like this one. It is necessarily to prevent the symbols from being evaluated at both levels of expansion:
user> (defmacro catch-me []
'(list 'catch 'Exception 'ex
'true))
user> (defmacro try-me []
`(try (+ 4 3)
~(catch-me)))
#'user/try-me
user> (try-me)
7
and to see that it catches the exception as well:
user> (defmacro try-me []
`(try (/ 4 0)
~(catch-me)))
#'user/try-me
user> (try-me)
true

Related

Does Clojure have a 'constantly' form that executes the body function at call-time?

I want to use constantly in a test to model a scenario that throws an Exception. Using the off-the-shelf constantly the body is evaluated when the code is read, not executed. I.e. I can't do this:
(def x (constantly (throw (Exception. "X"))))
(x 1 2 3)
Instead, the throw happens immediately.
This works:
(defn x [&] (throw (Exception. "X")))
But constantly is so handy and idiomatic, I wonder if there's a built-in equivalent that does this, maybe using a macro?
One alternative
#(throw (Exception. (str %&)))
constantly is a function not a macro like fn so you need to use (fn [& args]) to achieve this kind of operation.
constantly eagerly evaluates its parameters that's why it fails immediately.
It isn't built in, but it's easy to define. Let's call it defer:
(defmacro defer [exp]
(list 'fn ['& '_] exp))
Your example becomes
(def x (defer (throw (Exception. "X"))))
=> #'user/x
(x 1 2 3)
=> Exception X user/x (form-init7339591407440568822.clj:10)
This has no practical advantage over using the # reader form directly, as tap does, but it is what you asked for.
I changed the generated function to accept arguments, as the question called for. So it is no longer a thunk.
There are several ways to delay a computation in Clojure
The most obvious is delay:
(def x (delay (throw (ex-info "myException" {}))))
#x ;; exception is thrown
You could also use a lambda, similar to what would have to be done in other languages, or use laziness.
Given your code sample in the question, it looks like you are looking for something like this:
(defn x [& args]
(throw (ex-info "myException" {:args args})))
(try
(x 1 2 3)
(catch Exception e
(println "Exception! data is " (ex-data e))))
Note the use of ex-info and ex-data which could be useful to pass information.

How to wrap Exception in Clojure Macro?

I would like to wrap exception which has been thrown by system or user(does not matter) and force it to return some value.
I wrote macro for it but it does not work.
Macro:
(defmacro safe-fn
[form]
(try
`(do ~form)
(catch Throwable e
1)))
Usage: (safe-fn (throw (RuntimeException. "Try me!")))
Actual output: RuntimeException Try me! clojure-brave-and-true.core/eval2219 (form-init6122238559239237921.clj:1)
Desired output: 1
Macros are just functions that return code to be evaluated, so you could write safe-fn like this:
(defmacro safe-fn
[form]
`(try
~form
(catch Throwable ~'_
1)))
Example:
(safe-fn (throw (RuntimeException. "Try me!")))
;=> 1
See my answer to this question for more detail on macros, and specifically on using them to catch exceptions.
The macro with-exception-default from the Tupelo library does exactly what you want:
Default Value in Case of Exception
Sometimes you know an operation may result in an Exception, and you
would like to have the Exception converted into a default value. That
is when you need:
(with-exception-default default-val & body)
Evaluates body & returns its result. In the event of an exception the
specified default value is returned instead of the exception."
(with-exception-default 0
(Long/parseLong "12xy3"))
;=> 0
This feature is put to good use in tupelo.parse, where you will find
functions that work like this:
(parse-long "123") ; throws if parse error
;=> 123
(parse-long "1xy23" :default 666) ; returns default val if parse error
;=> 666

Clojure core.typed annotation for apply inside a 3rd-party macro

I'm using slingshot's throw+ macro to raise an exception that looks like:
(throw+ {:type ::urlparse})
The type checker doesn't like it:
Type Error (stream2es/http.clj:79:17) Bad arguments to apply:
Target: [String t/Any * -> String]
Arguments: (PersistentList String)
in: (clojure.core/apply clojure.core/format (clojure.core/list "throw+: %s" (clojure.core/pr-str %)))
Type Checker: Found 1 error
The macro in slingshot looks like:
(defmacro throw+
([object]
`(throw+ ~object "throw+: %s" (pr-str ~'%)))
([object message]
`(throw+ ~object "%s" ~message))
([object fmt arg & args]
`(let [environment# (s/environment)
~'% ~object
message# (apply format (list ~fmt ~arg ~#args))
stack-trace# (s/stack-trace)]
(s/throw-context ~'% message# stack-trace# environment#)))
([]
`(s/rethrow)))
I've tried various ann ^:no-check forms on apply and format and none works. Since it's a macro, I'm assuming I can't annotate it since it replaces the code that's there. But I also can't rewrite the code in the macro like was suggested in this other answer, because it's in a library. How do I gradually type in this case?
If you’re not able to rewrite the implementation of throw+, I suggest a wrapper macro like this.
(defmacro typed-throw+ [object]
`(let [o# ~object]
(t/tc-ignore
(throw+ o#))
(throw (Exception.)))) ; unreachable
;; other arities are an exercise ..
This way, you still type check the argument, and core.typed still thinks throw+ always throws an exception — it doesn't really know that, but the final throw clause allows core.typed to give the entire expression type Nothing.
The real answer should be we can improve apply to know that applying a non-empty list will satisfy at least one argument, however this answer should work today.

Unable to get random (doc) from a namespace

I want to display random (doc) page for some namespace.
The random function name I can get by:
user=> (rand-nth (keys (ns-publics 'clojure.core)))
unchecked-char
When I try to pass this to (doc) I get this:
user=> (doc (rand-nth (keys (ns-publics 'clojure.core))))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.Symbol clojure.core/ns-resolve (core.clj:3883)
I'm new to Clojure and I'm not sure how to deal with this... I tried to convert this into regexp and use (find-doc) but maybe there is a better way to do this...
Explanation
The problem here is that doc is a macro, not a function. You can verify this with the source macro in the repl.
(source doc)
; (defmacro doc
; "Prints documentation for a var or special form given its name"
; {:added "1.0"}
; [name]
; (if-let [special-name ('{& fn catch try finally try} name)]
; (#'print-doc (#'special-doc special-name))
; (cond
; (special-doc-map name) `(#'print-doc (#'special-doc '~name))
; (resolve name) `(#'print-doc (meta (var ~name)))
; (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
If you're new to Clojure (and lisps), you might not have encountered macros yet. As a devastatingly brief explanation, where functions operate on evaluated code, macros operate on unevaluated code - that is, source code itself.
This means that when you type
(doc (rand-nth (keys (ns-publics 'clojure.core))))
doc attempts to operate on the actual line of code - (rand-nth (keys (ns-publics 'clojure.core))) - rather than the evaluated result (the symbol this returns). Code being nothing more than a list in Clojure, this is why the error is telling you that a list can't be cast to a symbol.
Solution
So, what you really want to do is evaluate the code, then call doc on the result. We can do this by writing another macro which first evaluates the code you give it, then passes that to doc.
(defmacro eval-doc
[form]
(let [resulting-symbol (eval form)]
`(doc ~resulting-symbol)))
You can pass eval-doc arbitrary forms and it will evaluate them before passing them to doc. Now we're good to go.
(eval-doc (rand-nth (keys (ns-publics 'clojure.core))))
Edit:
While the above works well enough in the repl, if you're using ahead ahead-of-time compilation, you'll find that it produces the same result every time. This is because the resulting-symbol in the let statement is produced during the compilation phase. Compiling once ahead of time means that this value is baked into the .jar. What we really want to do is push the evaluation of doc to runtime. So, let's rewrite eval-doc as a function.
(defn eval-doc
[sym]
(eval `(doc ~sym)))
Simple as that.

Unable to resolve symbol: thrown?

What is the proper way to do the following in clojure?
(ns todo.test.models.task
(:use [clojure.test]))
(deftest main-test
(is (thrown? Exception (throw Exception "stuff")))
(is (not (thrown? Exception (+ 2 3))))
)
First testcase runs fine but the whole snippet returns "Unable to resolve symbol: thrown?"
is is a macro that looks for the symbol thrown? in its body and build tests.
thrown? is not actually a function you can call. The default behaviour of is fails the test if an exception is thrown that was not beeing looked for, so you can just remove the (not (thrown? from the above example and get the result you are looking for.
thrown? is a special assertion that must show up after is, so you can't nest it in other expressions, so in the context of the is macro, the second assertion will not understand the symbol thrown?.
You could just say:
(deftest main-test
(is (thrown? Exception (throw (Exception. "stuff"))))
(is (= 5 (+ 2 3))))
If an exception is thrown in (+ 2 3), clojure.test will report 1 :error and 0 :fail and dump the stack trace.
Also note that your (throw Exception "stuff") is incorrect - you need to construct the Exception correctly inside the throw.
Use doseq if you want to do it for many statements:
(testing "bla"
(doseq [x [1 2 3 4]]
(my-dangerous-func! x)))
I know this is an old question but..
In addition to the above answers, if you really want a not-thrown? assertion, you can extend the is macro by adding your own e.g.
(defmethod assert-expr 'not-thrown? [msg form]
;; (is (not-thrown? c expr))
;; Asserts that evaluating expr does not throws an exception of class c.
;; Returns the exception thrown.
(let [klass (second form)
body (nthnext form 2)]
`(try ~#body
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual nil})
(catch ~klass e#
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual e#})
e#))))
This should then work as your original expectations
((deftest main-test
(is (thrown? Exception (throw (Exception. "stuff"))))
(is (not-thrown? Exception (+ 2 3)))))
However, please note that clojure.test will always report an error if an exception occurs in your function but if you have a special use-case for this, there you go.