Another State of Clojure survey and "error messages" is still the number one priority for improvement.
A discussion on Slack led to a number of specific pain points for beginners around exceptions and error messages. The one I'd like to focus on here is to provide a beginner-friendly alternative to (pst)
.
Currently, (pst)
makes a reasonable attempt at demunging names, eliminating "noise", and shrinking the stack trace some -- but it still leaves a lot to be desired for beginners who struggle to understand certain exceptions and have difficulty navigating (Java-style) stack traces, especially compared to some other languages that make a big effort to provide user-friendly error messages and stack traces.
An example from Slack was:
(defn f [i]
(fn [j]
(/ j i)))
(run! #(% 1) (map f (range 3)))
The exception printed in the REPL is fine but (pst)
shows:
user=> (pst)
ArithmeticException Divide by zero
clojure.lang.Numbers.divide (Numbers.java:190)
user/f/fn--16571 (NO_SOURCE_FILE:3)
user/eval16576/fn--16577 (NO_SOURCE_FILE:1)
clojure.core/run!/fn--8906 (core.clj:7849)
...
The elided part mentions clojure.lang.ArrayChunk.reduce
and then has multiple references to clojure.core.protocols
stuff (and doesn't go deep enough by default to show the original call to clojure.core/run!
).
I think there's an opportunity here for a new clojure.repl/explain
function, taking the same arguments as pst
, that provides both a more detailed explanation of the failure and further reduces the noise in the stack trace that pst
currently displays.
Ideally, this could be implemented with some basic cleanup in core but with some dynamic hooks that allow other tooling to "install" additional expansion and/or cleanup so that the community can provide libraries and functionality that further improve this aspect of the beginner experience.
A dynamic hook into ex-str
, for example, would allow community-provided tooling to massage the exception message shown (both for the original REPL input and for both pst
and explain
) so that messages that beginners struggle with, such as class <whatever> cannot be cast to clojure.lang.IFn...
could be rewritten into beginner-friendly language (Expected a function - found a <whatever>
).
Similar dynamic hooks to filter stack frames, and to "print" them to strings, would allow much more friendly output for beginners.