better error messages > smaller stack traces

313 views
Skip to first unread message

Stuart Halloway

unread,
Feb 8, 2011, 9:01:38 AM2/8/11
to clo...@googlegroups.com
This conversation began on Twitter [1] but I want to continue it on the mailing list as it may be of broader interest.

The core team is very interested in improving error messages, and relatively less interested in more code to manipulate stack traces, for the following reasons:

(1) The language sits at the bottom, and must solve the problems nobody higher up can solve. Error messages are created at the point of an error, and if the language gets it wrong you may not have the information to fix it later. OTOH, any old library code can reduce a stacktrace.

(2) Better error messages are easy to implement [2]. Most oddball errors are in macroexpansion, so there is no runtime cost to doing extra work to improve error messages. These can be implemented as pure functions, so even a relative beginner could contribute a patch.

(3) The clojure.repl/pst macro in 1.3 already provides better control of stack trace spewage at the REPL.

Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].

Thanks,
Stu

Stuart Halloway
Clojure/core
http://clojure.com

Alex Ott

unread,
Feb 8, 2011, 9:19:24 AM2/8/11
to clo...@googlegroups.com
Hello Stuart

One of problem, that sometime arise (at least for me) - errors in code
that is performed lazily - stack trace contains data about point where
it's evaluated, not where it's created:

for example, let look to something like:

I have a function that combines several functions
(defn make-funcs [& funcs]
(fn [data]
(flatten (map #(% data) funcs))))

and function that use these data, something like:

(defn do-something [data]
((make-funcs [func1 func2]) data))

if I omit 'funcs' in map call in 'make-funcs', then I'll get following
trace when data will evaluated in do-something, but it will contain no
pointer to make-funcs:

XXXXXXX core [ERROR] - java.lang.RuntimeException:
java.lang.RuntimeException: java.util.concurrent.ExecutionException:
java.lang.IllegalArgumentException: Wrong number of args (1) passed
to: core$map
at clojure.lang.LazySeq.sval (LazySeq.java:47)
clojure.lang.LazySeq.seq (LazySeq.java:63)
clojure.lang.RT.seq (RT.java:450)
clojure.core$seq.invoke (core.clj:122)
clojure.core$dorun.invoke (core.clj:2450)
clojure.core$doall.invoke (core.clj:2465)
my_ns$do_something.invoke (my_ns.clj:189)

Maybe it's possible to keep information about where this lazy seq was created?

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@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+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

--
With best wishes,                    Alex Ott, MBA
http://alexott.net/
Tiwtter: alexott_en (English), alexott (Russian)
Skype: alex.ott

daly

unread,
Feb 8, 2011, 9:26:08 AM2/8/11
to clo...@googlegroups.com, da...@axiom-developer.org
On Tue, 2011-02-08 at 09:01 -0500, Stuart Halloway wrote:
> This conversation began on Twitter [1] but I want to continue it on
> the mailing list as it may be of broader interest.
>
>
> The core team is very interested in improving error messages, and
> relatively less interested in more code to manipulate stack traces,
> for the following reasons:
>
>
> (1) The language sits at the bottom, and must solve the problems
> nobody higher up can solve. Error messages are created at the point of
> an error, and if the language gets it wrong you may not have the
> information to fix it later. OTOH, any old library code can reduce a
> stacktrace.
>
>
> (2) Better error messages are easy to implement [2].

This seems questionable to me. My car will post a 27B/6 error
to the diagnostic interface which tells my mechanic that the
oxygen sensor has failed. For my mechanic that is a good error
message.

However, when my car stalls it would be useless to display 27B/6.
A more useful error message might be "Call a tow truck".

"Good" error messages are extremely context sensitive and are
relative to the mindset of the person. There are times when a
null pointer exception is very precise and times when it is
completely content-free. The null pointer could happen because
there was missing input somewhere else in the program. So even
for a specific programmer the exception might have no meaning.

So my question is: What is the audience for the error message?

In my experience the Symbolics machines had great error messages.
They would throw you into emacs editing the failing source with
the cursor pointed at the specific failing line. You could fix
the failing line and continue from the point of the error. So a
good error message would contain information that would allow
Slime to do the same thing. That would probably be the filename
and source line of the failure.

This could be automated by the compiler. The compiler could see
a class of symbols, such as 'error' or 'blather' or 'opine' and
automatically lay down code to output (file . line) pairs. Slime
could then use this information to open the file@line.


> Most oddball errors are in macroexpansion, so there is no runtime cost
> to doing extra work to improve error messages. These can be
> implemented as pure functions, so even a relative beginner could
> contribute a patch.
>
>
> (3) The clojure.repl/pst macro in 1.3 already provides better control
> of stack trace spewage at the REPL.
>
>
> Please let us know when you get a misleading error message from a
> macroexpansion, so we can make it better. Or contribute a patch along
> the lines of [2].
>
>
> Thanks,
> Stu
>
>
> Stuart Halloway
> Clojure/core
> http://clojure.com
>
>
> [1] http://twitter.com/marick/statuses/33760838540070912
> [2] https://github.com/clojure/clojure/commit/d694d6d45fb46195ae4de01aab9a2b9f9c06355f
>

Brian Marick

unread,
Feb 8, 2011, 10:42:14 AM2/8/11
to clo...@googlegroups.com

On Feb 8, 2011, at 8:01 AM, Stuart Halloway wrote:

>
> Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].
>

What medium is best?

-----
Brian Marick, Artisanal Labrador
Contract programming in Ruby and Clojure
Author of /Ring/ (forthcoming; sample: http://exampler.com/tmp/ring.pdf)
www.exampler.com, www.exampler.com/blog, www.twitter.com/marick

Stuart Halloway

unread,
Feb 8, 2011, 10:49:35 AM2/8/11
to clo...@googlegroups.com
Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].


What medium is best?

Discussion here on the mailing list is fine, or a JIRA ticket [1]. We follow both.

Thanks!
Stu

Brian Goslinga

unread,
Feb 8, 2011, 11:23:30 AM2/8/11
to Clojure
I have been working on a fork of Clojure to explore what can be done
w.r.t. error messages: https://github.com/qbg/clojure Currently it
fixes a misleading error message generated by (int [5]), it aligns the
IndexOutOfBoundsExceptions with those thrown by the collections
classes, and it explores the possibility of rewriting
ClassCastExceptions so they are a bit more friendly.

Brian

Jeff Rose

unread,
Feb 10, 2011, 10:35:45 AM2/10/11
to Clojure
Here's an error I just got that could be improved upon. It shows an
error in a file core.clj, but my current project uses many libraries
and there are multiple files named core.clj. How about putting the
full path to the file somewhere so we can jump straight to it?

Thanks,
Jeff

---------------------------

user=> (use 'clojure.stacktrace)
nil
user=> (require 'session.inst)
#<CompilerException java.lang.Exception: EOF while reading (core.clj:
651)>
user=> (e)
java.lang.Exception: EOF while reading
at clojure.lang.LispReader.readDelimitedList (LispReader.java:1043)
clojure.lang.LispReader$ListReader.invoke (LispReader.java:900)
clojure.lang.LispReader.readDelimitedList (LispReader.java:1051)
clojure.lang.LispReader$ListReader.invoke (LispReader.java:900)
clojure.lang.LispReader.readDelimitedList (LispReader.java:1051)
clojure.lang.LispReader$ListReader.invoke (LispReader.java:900)
clojure.lang.LispReader.read (LispReader.java:145)
clojure.lang.Compiler.load (Compiler.java:5854)
nil
user=>

On Feb 8, 3:01 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
> This conversation began on Twitter [1] but I want to continue it on the mailing list as it may be of broader interest.
>
> The core team is very interested in improving error messages, and relatively less interested in more code to manipulate stack traces, for the following reasons:
>
> (1) The language sits at the bottom, and must solve the problems nobody higher up can solve. Error messages are created at the point of an error, and if the language gets it wrong you may not have the information to fix it later. OTOH, any old library code can reduce a stacktrace.
>
> (2) Better error messages are easy to implement [2]. Most oddball errors are in macroexpansion, so there is no runtime cost to doing extra work to improve error messages. These can be implemented as pure functions, so even a relative beginner could contribute a patch.
>
> (3) The clojure.repl/pst macro in 1.3 already provides better control of stack trace spewage at the REPL.
>
> Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].
>
> Thanks,
> Stu
>
> Stuart Halloway
> Clojure/corehttp://clojure.com
>
> [1]http://twitter.com/marick/statuses/33760838540070912
> [2]https://github.com/clojure/clojure/commit/d694d6d45fb46195ae4de01aab9...

Jeff Rose

unread,
Feb 10, 2011, 11:05:56 AM2/10/11
to Clojure
Sorry for the reply spam, but I've just remembered another error
reporting issue: pre and post conditions. They are a great feature
and I'd like to use them more often, but the error messages they
produce are virtually useless in comparison to just writing your own
check with a custom exception. How about having optional error
messages for the conditions?

Thanks again,
Jeff

On Feb 8, 3:01 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
> This conversation began on Twitter [1] but I want to continue it on the mailing list as it may be of broader interest.
>
> The core team is very interested in improving error messages, and relatively less interested in more code to manipulate stack traces, for the following reasons:
>
> (1) The language sits at the bottom, and must solve the problems nobody higher up can solve. Error messages are created at the point of an error, and if the language gets it wrong you may not have the information to fix it later. OTOH, any old library code can reduce a stacktrace.
>
> (2) Better error messages are easy to implement [2]. Most oddball errors are in macroexpansion, so there is no runtime cost to doing extra work to improve error messages. These can be implemented as pure functions, so even a relative beginner could contribute a patch.
>
> (3) The clojure.repl/pst macro in 1.3 already provides better control of stack trace spewage at the REPL.
>
> Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].
>
> Thanks,
> Stu
>
> Stuart Halloway
> Clojure/corehttp://clojure.com
>
> [1]http://twitter.com/marick/statuses/33760838540070912
> [2]https://github.com/clojure/clojure/commit/d694d6d45fb46195ae4de01aab9...

Fogus

unread,
Feb 10, 2011, 11:33:20 AM2/10/11
to Clojure
> reporting issue: pre and post conditions.  They are a great feature
> and I'd like to use them more often, but the error messages they
> produce are virtually useless in comparison to just writing your own
> check with a custom exception.  How about having optional error
> messages for the conditions?

I'm not sure that I follow your meaning. Do you mind giving an
example?

Daniel Werner

unread,
Feb 10, 2011, 2:10:35 PM2/10/11
to clo...@googlegroups.com
On 10 February 2011 17:05, Jeff Rose <ros...@gmail.com> wrote:
> Sorry for the reply spam, but I've just remembered another error
> reporting issue: pre and post conditions.  They are a great feature
> and I'd like to use them more often, but the error messages they
> produce are virtually useless in comparison to just writing your own
> check with a custom exception.  How about having optional error
> messages for the conditions?

You can make your pre/post condition AssertionErrors more
human-readable by factoring out their logic into helper functions:

(defn valid-article-id? [art-id]
(or (and (> (mod x 42) 3) (frobz x)) (qux x)))

(defn buy-article [art-id]
{:pre [(valid-article-id? art-id)]}
...)

Thus, the AssertionError message will complain about
"valid-article-id?" failing instead of the rather cryptic "or and
mod". If you need even more flexibility, it is also possible to throw
an exception from the helper function. Not pretty, though.

That said, I'd really wish for the private clojure.core/assert-args
macro to be made public. It's very comfortable to work with and
provides for semi-standardized error messages (offending argument name
always in front).

Hubert Iwaniuk

unread,
Feb 10, 2011, 3:23:55 PM2/10/11
to clo...@googlegroups.com
Hi,

Jeff correct me if I'm wrong but I think you are after something along the following lines.
Instead:
(defn f [x] {:pre [(some-fancy-validation x)]} ..)

To have something like this:
(defn f [x] {:pre [^{:msg "here goes description of what has not been valid"} (some-fancy-validation x)]} ..)

It could turn precondition to a bit more user-friendly construct.

I would also like to have assert-args public.

Cheers,
Hubert.

Fogus

unread,
Feb 10, 2011, 3:33:16 PM2/10/11
to Clojure
> (defn f [x] {:pre [^{:msg "..."} (some-fancy-validation x)]} ..)

That idea (or some variation) is actually kinda nice.

Additionally, I've always hoped for separate
PreConditionAssertionError and PostConditionAssertionError types, but
keep forgetting to discuss it.

Jeff Rose

unread,
Feb 11, 2011, 7:03:25 AM2/11/11
to Clojure
Thanks Hubert, this is exactly what I'm talking about. D. Werner's
recommendation to use a function call for the validation is a good
point, but having a human readable message like this would be the most
clear.

Cheers,
Jeff

Daniel Werner

unread,
Feb 12, 2011, 10:30:52 AM2/12/11
to clo...@googlegroups.com
On 10 February 2011 21:33, Fogus <mef...@gmail.com> wrote:
> Additionally, I've always hoped for separate
> PreConditionAssertionError and PostConditionAssertionError types, but
> keep forgetting to discuss it.

A while ago Stuart Sierra wrote about using typed assertions in unit
testing. One of his points was to give test functions the ability to
explain why they fail, instead of simply returning false. Rich raised
some interesting arguments contra more exception types, and pro more
value-oriented exceptions:

http://stuartsierra.com/2010/07/19/typed-assertions#comment-43140

>> (defn f [x] {:pre [^{:msg "..."} (some-fancy-validation x)]} ..)
>
> That idea (or some variation) is actually kinda nice.

Personally, I find the suggested syntax a bit verbose. Consider this:

(defn mangle-fullname [firstname lastname]
{:pre [^{:msg "firstname must be a string of 10 to 50 characters"}
(valid-firstname? firstname),
^{:msg "lastname must be a string of 20 to 70 characters"}
(valid-lastname? lastname)]}
(body-that-may-be-shorter-than-above-conditions))

How about integrating these messages into the validator functions?
They are allowed to raise exceptions themselves:

(defn valid-firstname? [firstname]
(if-not (string? firstname)
(throw (AssertionError. "firstname must be a string")))
(if-not (<= 10 (count firstname) 50)
(throw (AssertionError. "firstname must be between 10 to 50
characters long"))
true))

Note the "true" return value, otherwise the precondition will raise an
assertion error itself. Of course, this is still overly verbose, but
at least the verbosity is taken away from mangle-fullname's
definition.

Bummer that 'assert doesn't take an optional failure message, by the
way. 'assert-args does:

(defn mangle-fullname [firstname lastname]
(assert-args mangle-fullname
(string?) firstname "a string for firstname"
(<= 10 (count firstname) 50) "firstname to be between 10 to 50
characters long"
...))

user=> (mangle-fullname "Adam" "West")
IllegalArgumentException: mangle-fullname requires firstname to be
between 10 to 50 characters long

This doesn't allow to factor out the error message into a separate
validator function, though.

Daniel

Ken Wesson

unread,
Feb 12, 2011, 4:24:06 PM2/12/11
to clo...@googlegroups.com
+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;

Jeff Rose

unread,
Feb 13, 2011, 1:13:01 PM2/13/11
to Clojure
Here's one:

user=> {:a 1 :b}
#<CompilerException java.lang.ArrayIndexOutOfBoundsException (REPL:3)>

Instead how about a parse error exception that specifically says
something like, "Map literal requires an even number of forms."

Thanks,
Jeff

On Feb 8, 3:01 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:
> This conversation began on Twitter [1] but I want to continue it on the mailing list as it may be of broader interest.
>
> The core team is very interested in improving error messages, and relatively less interested in more code to manipulate stack traces, for the following reasons:
>
> (1) The language sits at the bottom, and must solve the problems nobody higher up can solve. Error messages are created at the point of an error, and if the language gets it wrong you may not have the information to fix it later. OTOH, any old library code can reduce a stacktrace.
>
> (2) Better error messages are easy to implement [2]. Most oddball errors are in macroexpansion, so there is no runtime cost to doing extra work to improve error messages. These can be implemented as pure functions, so even a relative beginner could contribute a patch.
>
> (3) The clojure.repl/pst macro in 1.3 already provides better control of stack trace spewage at the REPL.
>
> Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].
>
> Thanks,
> Stu
>
> Stuart Halloway
> Clojure/corehttp://clojure.com
>
> [1]http://twitter.com/marick/statuses/33760838540070912
> [2]https://github.com/clojure/clojure/commit/d694d6d45fb46195ae4de01aab9...

Timo Mihaljov

unread,
Feb 14, 2011, 5:48:22 AM2/14/11
to clo...@googlegroups.com
On Tue, Feb 08, 2011 at 09:01:38AM -0500, Stuart Halloway wrote:
> Please let us know when you get a misleading error message from a
> macroexpansion, so we can make it better. Or contribute a patch along the lines
> of [2].

Here's a misleading lack of an error message:

(defn foo [x]
{:pre (odd? x)}
x)

The code may look fine at a glance, but the precondition is not
wrapped in a seq, so the actual preconditions become checks for
truthiness of `odd?` and `x`.

Maybe precondition forms should be required to be vectors, so that
function call forms can't be mistaken for lists of preconditions?

--
Timo

Ken Wesson

unread,
Feb 14, 2011, 6:24:54 AM2/14/11
to clo...@googlegroups.com

+1

OGINO Masanori

unread,
Feb 14, 2011, 6:53:23 PM2/14/11
to clo...@googlegroups.com
Hello.

There is an interesting model about error reporting: Clang, one of
C-family languages compiler which uses LLVM.
For example, if you mistake names, Clang searches similar names which
really exist in current environment.
And then Clang illustrates line, column and actual code.
If you want to get more informations, see [1].

Of course, they needs more works and error reporting may slow down.
Also, Clojure has non-preprocessor macro and JVM exception handling,
so we can't reproduce Clang truly.
However, we can learn some from Clang, I think.

Thank you.

[1] http://blog.llvm.org/2010/04/amazing-feats-of-clang-error-recovery.html

--
Name: OGINO Masanori (荻野 雅紀)
E-mail: masanor...@gmail.com

Timo Mihaljov

unread,
Feb 17, 2011, 6:12:14 AM2/17/11
to clo...@googlegroups.com
On Tue, Feb 08, 2011 at 09:01:38AM -0500, Stuart Halloway wrote:
> Please let us know when you get a misleading error message from a
> macroexpansion, so we can make it better. Or contribute a patch along the lines
> of [2].

Here's another error message that really threw me off for a while.

I have some code that looks like this:

(let [[x y] (nth @my-atom z)]
...)

Which occasionally failed with:

java.lang.UnsupportedOperationException: nth not supported on this type: Float
at clojure.lang.RT.nthFrom(RT.java:815) ~[clojure-1.2.0.jar:na]
at clojure.lang.RT.nth(RT.java:765) ~[clojure-1.2.0.jar:na]

I tried to figure out how a single float could have ended up in that
atom and why I couldn't catch it by checking for `(coll? @my-atom)`.
After a while I found out that `@my-atom` indeed contained a
collection -- a collection of floats -- and that the exception was
thrown by the destructuring.

An error message something like this would have been much more helpful:

Can't destructure `(nth @my-atom z)` to `[x y]`: expected a
collection with 2 or more items but got `Float`.

--
Timo

Stuart Halloway

unread,
Feb 25, 2011, 4:15:08 PM2/25/11
to clo...@googlegroups.com
> Here's one:
>
> user=> {:a 1 :b}
> #<CompilerException java.lang.ArrayIndexOutOfBoundsException (REPL:3)>
>
> Instead how about a parse error exception that specifically says
> something like, "Map literal requires an even number of forms."
>
> Thanks,
> Jeff

Fixed in master today: http://dev.clojure.org/jira/browse/CLJ-742

Thanks!

Stu

Mark

unread,
Feb 26, 2011, 8:14:28 PM2/26/11
to Clojure
I get this:
#<CompilerException java.lang.IllegalArgumentException: Wrong number
of args (3) passed to: Symbol (C:\Users\addma03\workspace\test\src\main
\clojure:1)>

A few suggestions:
1) An improved line number
2) I'd like to see the value of the Symbol
3) I'd like to see the three args applies to the symbol and, if the
symbol resolves to a function, I'd like to see the arity of the
function.

Something like:
Wrong number of args (3) passed to Symbol 'some-fn' which expects
arity 2

Ken Wesson

unread,
Feb 27, 2011, 2:10:05 PM2/27/11
to clo...@googlegroups.com
On Sat, Feb 26, 2011 at 8:14 PM, Mark <markad...@gmail.com> wrote:
> I get this:
> #<CompilerException java.lang.IllegalArgumentException: Wrong number
> of args (3) passed to: Symbol (C:\Users\addma03\workspace\test\src\main
> \clojure:1)>
>
> A few suggestions:
> 1)  An improved line number
> 2) I'd like to see the value of the Symbol
> 3) I'd like to see the three args applies to the symbol and, if the
> symbol resolves to a function, I'd like to see the arity of the
> function.
>
> Something like:
> Wrong number of args (3) passed to Symbol 'some-fn' which expects
> arity 2

This is interesting. I just checked with my copy of 1.2 and it seems
you can indeed call a symbol as a function. It appears to try to look
itself up in its first argument, the same as a keyword, and if not
found or the first argument is not a map returns the (optional) second
argument:

user=> ('+ {'+ 3 '* 4} 42)
3
user=> ('+ {'- 3 '* 4} 42)
42

Of course this is rather icky to use because of the need to quote the symbols.

So, the arity expected by a function like + ends up irrelevant. If you
invoke something on '+ it wants either one or two arguments.

If you're seeing this error you probably have a buggy macro that's not
unquoting something as many times as it should be. Quoting-depth
errors (other than forgetting to unquote something at all) seem to be
most common when writing macros that emit other macros, and ending up
with nested syntax-quoted expressions, doubled-up ~~@foo type
unquotings, and things like `(quote ~foo) in helper functions.

If the error points to a macro invocation I'd suggest inspecting the
output of (macroexpand-1 '(the-problem-invocation)) and seeing what
you get. If the output contains quoted symbols in operator position
that clearly are meant to be just normal calls to functions then
you've at least determined that the problem is caused by the macro.
Though it could be the case that your arguments to the macro violate
its preconditions. If it's not your own macro, check its
documentation. If it is it has a bug since it's not doing what you
want it to do and it's yours. If it's not it may or may not have a
bug.

Ken Wesson

unread,
Feb 27, 2011, 2:13:19 PM2/27/11
to clo...@googlegroups.com

Addendum: the most likely macro *argument* error to cause this is
quoting a function name macro argument that you shouldn't be quoting,
e.g. (the-macro 'my-func ...) instead of (the-macro my-func ...). The
macro just plops (quote my-func) wherever it should put my-func and
boom!

Mark

unread,
Feb 27, 2011, 2:50:51 PM2/27/11
to Clojure
I wrote that up quickly without thinking much about it. Yes, the
invocation is definitely a macro and not a function. Still, I think
it would be helpful to see the symbol's value and, if possible, the
macro's arity.

On Feb 27, 1:13 pm, Ken Wesson <kwess...@gmail.com> wrote:
> On Sun, Feb 27, 2011 at 2:10 PM, Ken Wesson <kwess...@gmail.com> wrote:

Daniel Werner

unread,
Feb 27, 2011, 3:13:03 PM2/27/11
to clo...@googlegroups.com, Mark
On 27 February 2011 20:50, Mark <markad...@gmail.com> wrote:
> I wrote that up quickly without thinking much about it.  Yes, the
> invocation is definitely a macro and not a function.  Still, I think
> it would be helpful to see the symbol's value and, if possible, the
> macro's arity.

Is calling symbols as a function actually used anywhere in Clojure or
in the real world? If not, it might make sense to remove this (rather
unexpected, IMHO) feature as a backwards-incompatible change.

Mark

unread,
Feb 27, 2011, 3:33:13 PM2/27/11
to Clojure
With a fresh brain (and a fresh cup of coffee), I realized this
message is probably caused (somehow) by my misuse of the midje
library. No doubt it does fancy macro stuff under-the-hood.

At the core of this problem is that I'm a naive client of this macro
library and by an innocent misuse, I am stuck trying to debug my
syntax error by commenting out various pieces, adding printlns to see
how far the compiler gets in evaluating expressions, and guessing what
could be causing the problem. One could certainly argue that the
macro should do more to detect syntax errors and report them
gracefully and I would agree. I do not believe that relieves any
responsibility from the compiler to report as much information as it
can when it detects an error.

It would be nice if the compiler would report the macro form it was
trying to expand. I see it being an IDEs job to add niceties like a
macro expander tool which would let me expand the macro step-by-step.

None of this should be taken as a knock against midje. I'm just
starting to learn it and the style of programming it fosters and I
like it very much!

Brian Marick

unread,
Feb 28, 2011, 10:06:32 AM2/28/11
to clo...@googlegroups.com

On Feb 27, 2011, at 2:33 PM, Mark wrote:

> With a fresh brain (and a fresh cup of coffee), I realized this
> message is probably caused (somehow) by my misuse of the midje
> library. No doubt it does fancy macro stuff under-the-hood.

Indeed it does.

> One could certainly argue that the
> macro should do more to detect syntax errors and report them
> gracefully and I would agree.

I've started work on better error handling. (Monads!) When you figure out what your syntax error was, please report it:
https://github.com/marick/Midje/wiki/Error-message-improvements

(Or report a bug in the tracker if it turns out you were right but Midje was wrong.)

Mark

unread,
Mar 1, 2011, 11:37:07 PM3/1/11
to Clojure
I found the problem:
('apply + 1 1)

I understand why this won't work if I tried to eval the list, but I
don't understand why I can't create a list of these symbols.

Alan

unread,
Mar 2, 2011, 3:32:08 AM3/2/11
to Clojure
'(apply + 1 1) would be how you create a list of those symbols.
('apply + 1 1) says "call the function 'apply with the arguments of +
1 and 1".
> > Author of /Ring/ (forthcoming; sample:http://exampler.com/tmp/ring.pdf)www.exampler.com,www.exampler.com/bl...

Ken Wesson

unread,
Mar 2, 2011, 8:19:31 AM3/2/11
to clo...@googlegroups.com
On Wed, Mar 2, 2011 at 3:32 AM, Alan <al...@malloys.org> wrote:
> '(apply + 1 1) would be how you create a list of those symbols.

Or (list 'apply '+ 1 1) or `(apply + 1 1), both of which allow you to
put something variable in there, like (list 'apply '+ 1 x) or `(apply
+ 1 ~x).

Mark

unread,
Mar 2, 2011, 9:16:01 AM3/2/11
to Clojure
Thanks!

On Mar 2, 5:19 am, Ken Wesson <kwess...@gmail.com> wrote:

Daniel Werner

unread,
Mar 3, 2011, 3:18:49 AM3/3/11
to clo...@googlegroups.com
On 2 March 2011 09:32, Alan <al...@malloys.org> wrote:
> '(apply + 1 1) would be how you create a list of those symbols.
> ('apply + 1 1) says "call the function 'apply with the arguments of +
> 1 and 1".

Note that this will still break at runtime because Integers are not seqable. :-)

You probably want either (apply + [1 1]) or (apply + 1 [1]).

Sam Aaron

unread,
Apr 14, 2011, 9:20:14 AM4/14/11
to clo...@googlegroups.com
Here's something that could be clearer (it wasn't obvious to me that something like addition would cause a null pointer exception):

user=> (+ 1 nil)
java.lang.NullPointerException (NO_SOURCE_FILE:0)

Sam

---
http://sam.aaron.name

On 8 Feb 2011, at 14:01, Stuart Halloway wrote:

> This conversation began on Twitter [1] but I want to continue it on the mailing list as it may be of broader interest.
>
> The core team is very interested in improving error messages, and relatively less interested in more code to manipulate stack traces, for the following reasons:
>
> (1) The language sits at the bottom, and must solve the problems nobody higher up can solve. Error messages are created at the point of an error, and if the language gets it wrong you may not have the information to fix it later. OTOH, any old library code can reduce a stacktrace.
>
> (2) Better error messages are easy to implement [2]. Most oddball errors are in macroexpansion, so there is no runtime cost to doing extra work to improve error messages. These can be implemented as pure functions, so even a relative beginner could contribute a patch.
>
> (3) The clojure.repl/pst macro in 1.3 already provides better control of stack trace spewage at the REPL.
>

> Please let us know when you get a misleading error message from a macroexpansion, so we can make it better. Or contribute a patch along the lines of [2].
>

> Thanks,
> Stu
>
> Stuart Halloway
> Clojure/core

> [2] https://github.com/clojure/clojure/commit/d694d6d45fb46195ae4de01aab9a2b9f9c06355f

Reply all
Reply to author
Forward
0 new messages