+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

Reply via email to