How to catch exception and keep looping?

66 views
Skip to first unread message

Konrad Kułakowski

unread,
Feb 12, 2010, 1:31:15 PM2/12/10
to Clojure
I have a piece of code like this:

(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

Michał Kwiatkowski

unread,
Feb 12, 2010, 3:52:09 PM2/12/10
to clo...@googlegroups.com
2010/2/12 Konrad Kułakowski <kulak...@gmail.com>:

> I have a piece of code like this:
>
> (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?

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

Michał Marczyk

unread,
Feb 12, 2010, 4:13:37 PM2/12/10
to clo...@googlegroups.com
2010/2/12 Konrad Kułakowski <kulak...@gmail.com>:

> (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

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ł

Adrian Cuthbertson

unread,
Feb 13, 2010, 1:38:12 AM2/13/10
to clo...@googlegroups.com
Here's an idiomatic way of doing what you want;

(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.

Konrad Kułakowski

unread,
Feb 13, 2010, 10:20:39 AM2/13/10
to Clojure
Thanks Adrian et al.

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.

Reply all
Reply to author
Forward
0 new messages