Clojure not catching NumberFormatException - clojure

In the following code, Clojure (1.2) is printing the wrong message:
(try
(let [value "1,a"]
(map #(Integer/parseInt %) (.split value ",")))
(catch NumberFormatException _ (println "illegal argument")))
This should print "illegal argument", but instead it prints a (1#<NumberFormatException java.lang.NumberFormatException: For input string: "a">.
What am I doing wrong?
Is this because of the lazy sequence returned by map? How should it be written?

The try special form only catches exceptions that are raised during during the dynamic extent of the body code. Here map is returning a lazy sequence, which then is passed out of the try special form and returned. The printer then evaluates the sequence, and at that point the exception is thrown.
Wrapping the map in doall should fix your problem.

Related

How to extract metadata from a Clojure Exception?

I'm just starting to learn Clojure and struggling to extract Exception metadata. When I run this:
(try
  (/ 1 0)
  (catch Exception error (println error)))
I get an ArithmeticException, as expected. The stacktrace printed looks like this:
#error {
:cause Divide by zero
:via
[{:type java.lang.ArithmeticException
:message Divide by zero
:at [clojure.lang.Numbers divide Numbers.java 188]}]
:trace
[[clojure.lang.Numbers divide Numbers.java 188]
[clojure.lang.Numbers divide Numbers.java 3901]
...
]}
It looks like a map to me, so I tried to extract the value from :cause with (:cause error), but it evaluates to nil.
How can I do that?
UPDATE:
After digging a bit, I found that #error {...} is a java.lang.Throwable class, is that correct?
I tried using Java interop (.getCause error), but also returns nil. Turns out (.getMessage) error) does return "Divide by zero".
Are there other ways to get specific attributes from that class, other than .getMessage()?
Clojure has ex-message to retrieve the message from an exception and ex-cause to retrieve the cause -- if there is one. The printed display of #error is a bit misleading here because there actually is no "cause" in the exception (in the Java sense of .getCause) because there is no chained exception.
Another useful function is Throwable->map which turns the exception (all exceptions are java.lang.Throwable at their root) into a regular Clojure hash map on which you can perform all the regular operations:
user=> (ex-message (try (/ 1 0) (catch Exception e e)))
"Divide by zero"
user=> (keys (Throwable->map (try (/ 1 0) (catch Exception e e))))
(:via :trace :cause)
user=> (:cause (Throwable->map (try (/ 1 0) (catch Exception e e))))
"Divide by zero"
user=>

Cannot Figure Out How to Remove String cannot be cast to clojure.lang.IFn From Small Clojure Function

I have a small clojure function:
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page] (cstr/split (extract-key assess-pro-acct) #"-")]
(list (cstr/trim book) (cstr/trim page))))
Given this: (extract-key assess-pro-acct) #"-"), the extract-key's value is :legal_ref. So, it is fetching a single value like 927-48 out of a map and splitting the value using '-'. I just need to catch when there isn't one of those nice values. That is where the split returns nil.
So, I am stuck having tried to replace the original function with the following.
(def missing-book 888)
(def missing-page 999)
.
.
.
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page] (cstr/split (extract-key assess-pro-acct) #"-")]
(let [[trimBook trimPage] ((if book (cstr/trim book) (missing-book))
(if page (cstr/trim page) (missing-page)))]
(list (trimBook) (trimPage)))))
The problem is I keep getting the dreaded
String cannot be cast to clojure.lang.IFn From Small Clojure Function
error. How can I restructure this function to avoid the error?
Post Answers Edit:
Thank you for the answers:
I reworked the function to test for a "-" in a string. If it's not there, I use a dummy "888-99" as a value when none is there.
(def missing-book-page "888-99")
.
.
.
(defn split-legal-ref
"Highly specific function that expects one AssessPro map and a map key,
from which book and page will be extracted."
[assess-pro-acct extract-key]
(let [[book page]
(if (.contains "-" (extract-key assess-pro-acct))
(cstr/split (extract-key assess-pro-acct) #"-")
(cstr/split missing-book-page #"-"))]
(list (cstr/trim book) (cstr/trim page))))
You have an extra set of parentheses around the expression beginning with ((if book .... The if expression returns a string, and then since that string is in the first position of a list with the outer of those 2 parentheses, Clojure tries to invoke the string as a function.
Parentheses are very, very significant in Clojure. Unlike arithmetic expressions in languages like Fortran, C, C++, Java, Python, etc., where adding an extra set of parentheses around a subexpression is redundant, and maybe bad style, but harmless, it changes the meaning of Clojure expressions.
Can you add more information, like the function names and sample data? Also include more of the error message.
Somewhere in your code you are attempting to use a string as if it were a function. For example:
("hello" 3) ; should be (inc 3) or something. This is line #6
This generates the following error
ERROR in (dotest-line-5) (core.clj:6)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.ClassCastException: class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
at tst.demo.core$fn__18295.invokeStatic (core.clj:6)
<snip>
Note the last line of the error above refers to core.clj:6 which matches the namespace tst.demo.core and line number 6 where (hello 3) is found in the source code.

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

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

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

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.