Clojure error reporting is a problem. This gist contains examples of missed opportunities for a clear error message. Let's discuss how common scenarios can be improved, and get some "patch welcome" tickets out there with clearly defined expectations.
Helpful error reporting is a solvable problem, one which will benefit a large user base.
The top three Clojure specific questions on Stack Overflow are:
- "How do you make a web application in Clojure?"
- "Debugging in Clojure?".
- "How can I make nrepl-ritz-jack-in work remotely over TRAMP / Emacs" (ritz is a debugging tool)
The top rated Haskell debugging tagged question is ranked 91st.
Great advice about error handling is given in Illuminated Macros.
(do
(map println (range 10))
(println "done"))
done
Why not "unused lazy-seq on line n"
(def r (ref 0))
(dosync alter r inc)
#<core$inc clojure.core$inc@e2a01da>
Why not "unused expression alter", "unused expression r"
(def ^:dynamic *x*)
(println
(binding [*x* 1]
(for [i (range 5)]
(+ i *x*))))
Unhandled java.lang.ClassCastException
clojure.lang.Var$Unbound cannot be cast to java.lang.Number
Why not tell me that x is unbound?
(println
(with-open [r (clojure.java.io/reader "/etc/hosts")]
(map clojure.string/trim (line-seq r))))
Unhandled java.io.IOException
Stream closed
Why not tell me r is closed? Why not tell me I have a leaking lazy-seq?
(let [baz 1
[foo bar] baz]
(+ foo bar))
Caused by java.lang.UnsupportedOperationException
nth not supported on this type: Long
An accurate error message that could be more helpful: Why not "destructuring baz failed: nth not supported on Long" There is a ticket but the patch is not a good one and the issue is still open.
(let [{:keys [a b]} 1]
a)
Caused by java.lang.UnsupportedOperationException
Can't type hint a primitive local
I don't get it.
(let [a 1
b 2]
(assert (= a b)))
Caused by java.lang.AssertionError
Assert failed: (= a b)
Yes, I know, that's why I added the assertion, what are a and b please?
(defn foo [x]
{:pre (pos? x)}
x)
(foo 0)
0
No, this is an error, :pre must be a sequence.
(defn foo2 [x]
{:pre [(pos? x)]}
x)
(foo2 0)
Caused by java.lang.AssertionError
Assert failed: (pos? x)
Yes, but what is x?
(use 'clojure.test)
(deftest (is true))
Caused by java.lang.RuntimeException
First argument to def must be a Symbol
(ftypo 1)
Cannot call method 'call' of undefined
Why not tell me more plainly ftypo is undefined? Why would this code ever compile? When auto-build fails, it should delete the target so I don't think it worked.
The Eastwood README has excellent documentation of common problems, and can detect and report them. However it is not part of the compile chain, so is never seen.