+1 for the assertion macro suggestions (custom messages). On the topic of stack traces: it's high time Clojure stopped generating shit like
java.lang.RuntimeException: java.lang.RuntimeException: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Integer (NO_SOURCE_FILE:3545) at clojure.lang.Compiler.eval(Compiler.java:5440) at clojure.lang.Compiler.eval(Compiler.java:5415) at clojure.lang.Compiler.eval(Compiler.java:5391) at clojure.core$eval.invoke(core.clj:2382) at com.example.yourns$eval39664.invoke(NO_SOURCE_FILE:354) blah, blah, blah at java.lang.Thread.run (Thread.java:616) Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Integer at clojure.core$foo at clojure.core$bar at some.package.SomeJavaClass.quux(SomeJavaClass.java:666) ... 17 more Caused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Integer at clojure.lang.RT.seqFrom(RT.java:471) at clojure.lang.RT.seq(RT.java:452) at clojure.lang.RT.cons(RT.java:534) at clojure.core$cons.invoke(core.clj:28) ... 14 more without any of the visible stack trace lines containing any reference to your own project except for at com.example.yourns$eval39664.invoke(NO_SOURCE_FILE:354) which just refers to the REPL expression you tried to evaluate which test exposed the bug, not the line of source containing the bug itself. The crucial lines always seems to start one past the end of what the last stack trace shows, in this case the very next line quite likely would have been a line of the project trying to cons onto an integer (which was probably a bad argument it got from its caller, mind). I suggest altering the default behavior of the REPL in printing stack traces to compress all but the LAST in a chain of "Caused by:" exceptions. Usually the last one is the critical one, not the first. In the meantime I offer this tool: (defn get-ultimate-cause [e] (if-let [c (.getCause e)] (recur c) e)) (defn p-relevant [e] (println (.toString e)) (doseq [elt (.getStackTrace e)] (let [cn (.getClassName elt)] (if-not (or (.startsWith cn "clojure.") (.startsWith cn "java.")) (println " " (.toString elt)))))) (defmacro ttry [& body] `(try (eval (quote (do ~@body))) (catch Exception e# (p-relevant (get-ultimate-cause e#))))) The eval is so it catches both compiler exceptions (e.g. undefined symbol) and runtime ones (e.g. ClassCastException passing arg of wrong type somewhere). It prints just the parts of the stack trace of the final cause exception you're probably interested in: the ones outside the various clojure.foo and java.foo namespaces (so, the ones in your own project, and maybe Swing methods and such, and third-party library entries). That gets rid of much of the clutter and shows the various lines within your own code that were involved in the error. Incidentally, I uncovered a bug in Clojure 1.2(!) while testing this: (defmacro ttry [& body] `(try (eval (quote (do ~@body))) (catch Exception e# (let [c# (loop [e# e#] (if-let [c# (.getCause e#)] (recur c#) ; First bug site e#))] (println (.toString c#)) (doseq [elt# (.getStackTrace c#)] (let [cn# (.getClassName elt#)] (if-not (or (.startsWith cn# "clojure.") (.startsWith cn# "java.")) (println " " (.toString elt#))))))))) ; Second bug site Both compiler exceptions are java.lang.UnsupportedOperationException: Cannot recur from catch/finally But in neither location is there an attempt to "recur FROM catch/finally"; in the first instance, the recur should go back to a loop entirely contained within the catch, so not out of the catch, and in the second instance, I can only presume that the doseq expands into a loop that would also be entirely contained within the catch. Things like (loop [x 256 out []] (if (> x 0) (try (let [y (do-something-dangerous-only-when-x-is-100 x)] (recur (dec x) (conj out y))) (catch Foo e (recur 42 out))) out)) are clearly what're supposed to be verboten here -- and even then I'm not sure why, as this equivalent Java with a continue in a catch is legal: List<Bar> out = new ArrayList<Bar>(); for (x = 256; x > 0; x--) { try { Bar y = doSomethingDangerousOnlyWhenXIs100(x); } catch (Foo e) { x = 42; continue; } out.add(y); } return out; -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en