That's one of the drawbacks of C++. I really love it but sometimes a tiny syntax error will produce so many errors that you can't even scroll to the top of the console to find the cause.
clojure/clojurescript has very similar issues. A small syntax error (e.g. forgetting the vector in a :keys unpacking — putting the bare field name) will throw up hundreds of lines of unreadable stack trace.
I hear this complaint a lot about Clojure, but I have to admit that I don't fully understand it.
My usual experience with Clojure has been that the stack traces are long... but the exception error messages are generally pretty good, and the stack frames that correspond to user code generally have good information on the position of the offending statement.
Maybe you could make an argument that the internal Clojure stack frames should be hidden from stack traces, but Java itself makes that difficult. The mechanisms that `Throwable` uses for capturing and printing the stack back trace are both private methods of the class. Working around this would involve identifying every (most?) places in a codebase where a Clojure stack trace might be printed and then using custom Clojure-specific stack trace printer. This might be doable for stack traces printed by the REPL or compiler, but very difficult for stack traces printed by an external linked-in logging framework.
Debuggers can be helpful by disguising/translating details, but I'd be very cautious about a feature that messed with the stack frame. Sometimes that's what's being debugged! It would be a disservice to muddy that information.
Most of us are pretty good at scanning down a stack to find our own stuff.
> My usual experience with Clojure has been that the stack traces are long... but the exception error messages are generally pretty good, and the stack frames that correspond to user code generally have good information on the position of the offending statement.
I do not agree, but please note my comment isn't talking about "running" stack traces here it's talking about the compiler blowing up on what is essentially a syntax error:
(let [{:keys foo} {:foo 1}]
(print foo))
note how `foo` is bare rather than in a vector. Put that in a script, `clj -m` it
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(hello.clj:5:3)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6567)
at clojure.lang.Compiler.analyze(Compiler.java:6361)
at clojure.lang.Compiler.analyze(Compiler.java:6322)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5708)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5139)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3751)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6558)
at clojure.lang.Compiler.analyze(Compiler.java:6361)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6548)
at clojure.lang.Compiler.analyze(Compiler.java:6361)
at clojure.lang.Compiler.access$100(Compiler.java:37)
at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:529)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560)
at clojure.lang.Compiler.analyze(Compiler.java:6361)
at clojure.lang.Compiler.analyze(Compiler.java:6322)
at clojure.lang.Compiler.eval(Compiler.java:6623)
at clojure.lang.Compiler.load(Compiler.java:7064)
at clojure.lang.RT.loadResourceScript(RT.java:370)
at clojure.lang.RT.loadResourceScript(RT.java:361)
at clojure.lang.RT.load(RT.java:440)
at clojure.lang.RT.load(RT.java:411)
at clojure.core$load$fn__5018.invoke(core.clj:5530)
at clojure.core$load.doInvoke(core.clj:5529)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invoke(core.clj:5336)
at clojure.core$load_lib$fn__4967.invoke(core.clj:5375)
at clojure.core$load_lib.doInvoke(core.clj:5374)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invoke(core.clj:619)
at clojure.core$load_libs.doInvoke(core.clj:5413)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invoke(core.clj:619)
at clojure.core$require.doInvoke(core.clj:5496)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.main$main_opt.invoke(main.clj:335)
at clojure.main$main.doInvoke(main.clj:440)
at clojure.lang.RestFn.invoke(RestFn.java:436)
at clojure.lang.Var.invoke(Var.java:423)
at clojure.lang.AFn.applyToHelper(AFn.java:167)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
at clojure.lang.RT.seqFrom(RT.java:505)
at clojure.lang.RT.seq(RT.java:486)
at clojure.core$seq.invoke(core.clj:133)
at clojure.core$reduce1.invoke(core.clj:890)
at clojure.core$destructure$pb__4541$pmap__4544$fn__4547.invoke(core.clj:4013)
at clojure.core$reduce1.invoke(core.clj:896)
at clojure.core$destructure$pb__4541$pmap__4544.invoke(core.clj:4014)
at clojure.core$destructure$pb__4541.invoke(core.clj:4028)
at clojure.core$destructure$process_entry__4557.invoke(core.clj:4030)
at clojure.core$reduce1.invoke(core.clj:896)
at clojure.core$destructure.invoke(core.clj:4033)
at clojure.core$let.doInvoke(core.clj:4046)
at clojure.lang.RestFn.invoke(RestFn.java:467)
at clojure.lang.Var.invoke(Var.java:427)
at clojure.lang.AFn.applyToHelper(AFn.java:172)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6468)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6546)
... 40 more
Yeah… the third time around you might instantly know what it is, the first time around when it's a few levels down a type method not so much.
The first line of the error message tells you where and what the error is, almost exactly:
... Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(hello.clj:5:3)
It's true that the full interpretation of the error message requires some experience with the language (ISeq? Symbol?), but when is that not the case? Consider this one character typo in a Java file:
voidx displayMessage(String message);
javac produces an error message that's just about as opaque as what Clojure produces in your example.
[ERROR] ... MessageSink.java:[5,4] error: cannot find symbol
Conversely, if I leave an open brace in Java source, I get the following kind of error from javac:
[ERROR] ... ConsoleMessageSink.java:[12,1] error: reached end of file while parsing
If I leave an open form in Clojure, it at least tells me the location of the form I left open:
Exception in thread "main" java.lang.RuntimeException: EOF while reading, starting at line 18, compiling:(toto/data.clj:255:1)
(Of course, thanks to paredit-mode, I had to play a minor trick on my editor to get it to let me even introduce that error in the first place.)
Then you simply never tried any other modern language than Python.
Python's error messages are pretty OK, but they're nothing special. I'd say that in general, evil yucky unstartuppy Enterprise languages like Java and C# do slightly better.
I think you did not understand moccajoghurt's point. Because the errors cascade - that is, the one syntax error causes many, many (many) more errors, there are so many errors that it's hard to scroll up to the first error.
It's worse. In C++, that first error often is a red herring. The classical example is a header file where one forgets to type a semicolon. In classical C++ compilers, that triggered errors pointing to the file that includes the erroneous header.
Clang developers have worked hard to improve that; see http://clang.llvm.org/diagnostics.html, section "Quality of Implementation and Attention to Detail".
While I have trained myself to look for "clusters of errors" instead of individual errors, in my experience, the first error g++ reports tends to be the real cause. (I've also been programming in C++ long enough that I often don't even bother reading the error message - it's usually quicker if I just go to that line of code, and look for what's wrong.)
I tried some of the examples with g++/cc and with clang. Clang is a lot better but it still produces some pretty ridiculous output (but not gigs of ridiculous output fortunately).
Oh, I totally agree. That said, these examples are engineered to make your compiler shit a brick. Most of the time clang++ gives you much more readable output. g++, however, is a lot more careful about your digits. It'll take a little while before clang++ gains serious traction in the scientific community.
g++ has improved their error messages in the past few years, especially when it comes to template error messages. But C++ is still really complicated and you can get get pretty nasty errors.
And clang++ may be better in some aspects but it is not perfect either, one of the entries in this contest caused clang++ to segfault.