(def ll ['a 'b 'c 'd])
(loop [e (first ll) f (rest ll)]
(do
(try
(do
(println e) ;; do sth with e which may throw an exception
(recur (first f) (rest f)))
(catch Exception _ (println "ex")))
(println "something")
(recur (first ll) f (rest ll))
))
as a result I've got a message:
java.lang.IllegalArgumentException: Mismatched argument count to
recur, expected: 0 args, got: 2
So, my question is how to catch exception and do something with them
and keep looping?
Does anyone know any solution/workaround of this problem?
Konrad
You're clearly doing something for side effects, so why not use doseq instead?
(def alist ['a 'b 'c 'd])
(doseq [e alist]
(try
(println e) ;; may throw exception here
(catch Exception _ (println "ex"))))
Cheers,
mk
Regardless of how the code can be restructured -- Michał has a good
proposition here -- I find this exception entirely baffling.
Apparently the first recur -- the one inside the do inside the try
block -- expects no arguments and when amended accordingly to (recur)
will happily proceed to loop infinitely... What gives?
Sincerely,
Michał
(defn lp [col]
(loop [ll (seq col)]
(when ll
(try
(let [itm (first ll)]
(if (= itm 'x) (throw (IllegalArgumentException.
(str itm " not supported.")))
(println (str "done item: " itm))))
(catch Exception ex (println (.getMessage ex))))
(recur (next ll))))
(println "done loop"))
user=> (lp ['a 'b 'x 'd])
done item: a
done item: b
x not supported.
done item: d
done loop
One can use (doseq for a simple example like this, but the above
pattern is useful for doing complex things in the outer loop, eg
accumulating collections, iterating multiple reads in parallel, etc.
Here's a more complex but realistic example; Say we want to loop
through some collection, and for each item, read the next line in a
file, process it (which may throw an exception), write the result to
an output file and accumulate only the successful items in a vector
which is returned to the caller...
(defn lpx
[col f-in f-out]
(with-open [fin (BufferedReader. (FileReader. (File. f-in)))
fout (PrintWriter. f-out)]
(loop [ll (seq col) rec (.readLine fin) cum []]
(if (and ll rec)
(let [itm (first ll)
cum (try
(if (= itm 'x) (throw (IllegalArgumentException.
(str rec " error, ignored.")))
(do (.println fout (str itm ":" rec))
(conj cum [itm rec])))
(catch Exception ex
(do (.println fout (str itm ":" (.getMessage ex))) cum)))]
(recur (next ll) (.readLine fin) cum))
cum))))
cat f1.txt
one
two
three
four
five
user=> (lpx ['a 'b 'c 'd] "f1.txt" "f2.txt")
[[a "one"] [b "two"] [c "three"] [d "four"]]
cat f2.txt
a:one
b:two
c:three
d:four
user=> (lpx ['a 'b 'x 'd] "f1.txt" "f2.txt")
[[a "one"] [b "two"] [d "four"]]
cat f2.txt
a:one
b:two
x:three error, ignored.
d:four
(There are functions in contrib which could also be used, but the
above illustrates reasonably core java interop, which is often
required in practice).
Just a clarification on the try/catch above - we're setting the local
cum to either the value calculated in the try or the one returned in
the catch. This is better illustrated below;
(defn xx [cum]
(let [cum (try (if (= cum :x)
(throw (Exception. "err"))
cum)
(catch Exception ex [:err (.getMessage ex)]))]
cum))
user=> (xx :a)
:a
user=> (xx :x)
[:err "err"]
-Rgds, Adrian.
I am going to remove recur from try catch special form. (BTW: doseq is
not the case since I need to modify freely the collection during
looping)
On the other hand I am curios whether "no recur inside the try-catch
special form" might be adopted as a "rule of thumb". It is interesting
since there are some cases when it works perfectly e.g.:
(def ll ['a 'b 'c 'd])
(loop [e (first ll) f (rest ll)]
(try
(cond
(= e nil)
nil
(= 0 0)
(do
(println e)
(recur (first f) (rest f))))
(catch Exception _ (println "ex"))))
In other words it is OK if there is nothing more inside the loop than
try-catch special form. But, adding a side effect methods to loop body
starts the problem.