
Compiler Errors for Humans - luu
http://elm-lang.org/blog/compiler-errors-for-humans
======
Animats
Some of these are not so much "human centered" issues as not cutting corners
in the compiler front end.

Many years ago I wrote the front end of a compiler-like system (it was for
formal specifications, not for runnable code) and dealt with some of these
problems. Whenever a type problem was detected, the error was reported and the
type of the failed object was changed to an internal error type. For any error
in which an error type was involved, no message was generated. This avoided
error cascading, something GCC inflicted on its users for decades.

For parse errors, display the line in error and mark correctly the item
involved in the error. Don't just display the index into the source stream at
the point the error was detected; that's often a token or two beyond the
problem. Work back to the point at which things stopped making sense to the
parser. You have to carry source position info with each token, but it's worth
it.

For errors which represent an inconsistency between several parts of the
source code, show all the places that conflict, not just one side of the
conflict. Rust is good about this. They have to be, because the borrow checker
reports inconsistencies between different code sections, not just declarations
and uses.

~~~
Someone
It also helps if your syntax supports error recovery. For example, if 'if'
statements end with 'fi', 'do' statements end with 'od', and function
definitions statements end with 'end', and you get 'if ... do ... fi', you can
report that you are missing a 'od' in the program, and recover parsing after
the 'fi'.

C is horrible in this respect; all nesting looks the same. Because of that it
took clang lots of effort to get good error recovery, for example to correctly
report a missing semicolon at the end of a header file instead of reporting an
error in the file including it ([http://blog.llvm.org/2010/04/amazing-feats-
of-clang-error-re...](http://blog.llvm.org/2010/04/amazing-feats-of-clang-
error-recovery.html) (NB: that page is 5 years old, and does not represent the
current state of gcc))

gcc makes that job even harder by allowing nested function definitions. That
means that accidentally forgetting a single '}', as in

    
    
       void f( int i) {
         if( i > 2) {
           exit(EXIT_FAILURE);
       }
       void g() {}
       [...]
       void h() {}
    

will make the compiler think that g() and h() are nested function definitions.
So, the error you get is a “missing closing brace" on the last line of your
file. Without nested function support, the error would be reported on the
'void g() {}' line. Still incorrect, but potentially thousands of lines closer
to the source.

The Algol compiler (helped by Algol's syntax) I used years ago was way better.
It frequently gave errors of the form

    
    
      "x undeclared. Assumed real"
    

(if, say, you call sin(x) without declaring x) or

    
    
      "semicolon missing after 'end' (inserted)”
    

Compilation would still fail after such errors, but you often would get
meaningful error messages for the entire program. That was quite a boon if the
compilation is run in batch, but it still is a good idea nowadays.

~~~
eru
Another option is to make indentation meaningful to fix C's problem here.

~~~
panic
You could use the indentation as a hint for error reporting even without
giving it syntactic meaning.

~~~
eru
Good and workable compromise! Perhaps even make it a proper warning, even if
inconsistent indentation is the only thing wrong.

------
rnhmjoj
I apologize in advance but I couldn't not bring this up: it's too hilarious.

About a year ago there was a bug in the elm compiler that occasionally caused
the types in a type mismatch error message to be swapped. For example the
compiler told you:

    
    
        Something weird is happening with this value:
    
           x
    
        Expected Type: number
          Actual Type: String
    

but it actualy meant the opposite:

    
    
        Expected Type: String
          Actual Type: number
    

Evan said it wasn't so easy to fix. You can see the issue here:
[https://github.com/elm-lang/elm-compiler/issues/216](https://github.com/elm-
lang/elm-compiler/issues/216)

At the time I was trying to learn elm (and functional programming) and I
didn't know it was a bug so it gave me a lot of troubles: I could never fully
trust the compiler.

Then I left elm and focused on haskell but now I think I see how he solved the
bug:

    
    
        As I infer the type of values flowing through your program, I see a conflict
        between these two types:
    
            String
    
            number
    

Anyway these new messages are very helpful, especially when compared to
haskell error like:

    
    
        Could not deduce (b ~ c)
        from the context (Num b, Num c)
        bound by the type signature for
              f :: (Num b, Num c) => [a] -> (b, c)
          at restriction.hs:(4,1)-(5,32)
          `b' is a rigid type variable bound by
              the type signature for f :: (Num b, Num c) => [a] -> (b, c)
    
    

Great job.

~~~
Nitramp
That's a common problem with globally inferencing compilers, you strictly
cannot know who's right about a type.

I think that's a good reason to prefer a language with local type inference,
but no type inference across function boundaries. It saves you from too much
boiler plate to type and has (IMHO) better developer ergonomics.

~~~
renox
In Jay (a language not finished) there is a prefix for the parameter who is
the 'master' type (if memory serves '$'), this has the following benefits: -in
fun f(a:$T, b:T) and f(1, 'hello') you can say Error parameter 'b' is a string
but generic type T is int as defined by parameter 'a'.

-no need to have a separate definition of generic types as in fn f<T>(a:T): fn f(a:$T) tells you already which type is generic.

------
steveklabnik

        > It is kind of shocking how much better things get when you focus on the user.
    

So true. We've seen this in Rust as well: every bit of time we spend working
on better diagnostics makes Rust so much more wonderful to use. Even a small
reduction in jargon helps new people out a ton. Diagnostics aren't
particularly exciting, but they're really important and your users will love
you for them.

------
Gracana
> I have met a few folks who switched from gcc to clang mostly because of a
> feature like this!

I'm in that camp. Both compilers function properly for what I need to do, but
clang's output is a lot friendlier. The choice to switch was easy to make.

~~~
arielb1
New versions of g++ (4.9+) have much better error messages (g++ has better
errors than gcc).

~~~
doomrobo
Are template-related errors also better? I remember being deathly afraid of
messing up something that uses std::string because I would just get back pages
and pages of errors (or pages and pages of a single error).

~~~
JoeAltmaier
I sure hope so. C++ templates are the worst thing ever for debuggability. And
all the most useful C++ containers etc are templates. That's the Achilles heel
of C++.

------
brudgers
Based on my [limited] understanding, better error messages is one of the goals
of Perl 6 and something the Racket folks put a lot of thought behind when
creating the Student Language series. At the other end of the spectrum are JVM
languages that fail spectacular with references to Java classes that the
program's author never heard of.

~~~
mhd
Databases are another example of this, with Postgres actually giving useful
hints, whereas Oracle just gives you cryptic numbered messages. If I never
have to read "missing right parenthesis" again…

~~~
bluetech
Heh, a long time ago I wrote a script using BeautifulSoup which given an ORA-
XXX would return a detailed explanation. Amazingly, it still works:
[https://gist.github.com/bluetech/f9aa4ede5a25c765c6e6](https://gist.github.com/bluetech/f9aa4ede5a25c765c6e6)
(don't hold me to the code though :)

------
Roodgorf
This is some very impressive stuff, I look forward to trying it out. Question:
Have you run into any cases where the hints have actually caused significant
roadblocks to discovering the underlying problem? I can't count the number of
times a compiler error has sent me down the wrong path entirely, I wonder how
much worse/better this is with more human-readable hints.

~~~
rtfeldman
I haven't yet. Before this was released I was using the bleeding edge compiler
at home and the stable version at work[0], and the bleeding edge version with
the nicer messages was always strictly faster for debugging.

In one particular case I recall racking my brain to figure out what I was
doing wrong at work, and then I went home and rebuilt with the new error
messages, and immediately saw the problem.

Highly recommend! :D

[0] We use Elm in production at [http://noredink.com](http://noredink.com) \-
and by the way, we're hiring!

------
to3m
I'm a bit sad that "editor takes to you location of compile error" is actually
touted as a feature! It's one thing to have to do this occasionally, but I'm
sorry to hear that people are used to doing it all the time :(

Nevertheless the error message point is good. It's ridiculous how atrocious
error messages can be. (And they probably seem even worse if you have to find
the file+line yourself.) This is actually pretty straightforward to implement,
and supported directly with flex+bison, so it's a shame that it's not more
commonplace.

Perhaps in the early days of gcc it was seen as too much overhead? Anyway,
should be de rigeur for anything being embarked upon today.

~~~
coldpie
> I'm a bit sad that "editor takes to you location of compile error" is
> actually touted as a feature! It's one thing to have to do this
> occasionally, but I'm sorry to hear that people are used to doing it all the
> time :(

Can you clarify what your objection is?

~~~
superuser2
This has been a standard feature of an IDE since forever; it's disappointing
that the plain-text editor community has just cracked it.

~~~
Narishma
What do you mean 'just'? It's been in emacs and vim as far back as I can
remember. At least for C and C++.

~~~
superuser2
"Just" as in it's being listed as a feature rather than a baseline
expectation, I think is parent's point.

------
rudolf0
Very impressive work. I feel like these kind of helpful error messages will
become the norm within 10 years, and people will look back at coredumps, C++
exceptions, and even Ruby/Python exceptions as confusing and archaic.

~~~
vezzy-fnord
Core dumps are at a completely different layer, so they don't really belong in
this comparison. Unless we phase out the traditional conception of an OS
process in 10 years, which is extremely doubtful.

~~~
rudolf0
They are a different layer, but they're the only effective way of debugging
most C programs.

~~~
AnimalMuppet
Horse feathers. Running a debugger, logging, and printf are all much more
effective than core dumps.

A core dump may be the most effective way to debug a program that dumped core
when crashing in a way that you can't reproduce, but that's because it's the
_only_ way to debug a program that dumped core when crashing in a way that you
can't reproduce.

~~~
rudolf0
Debugging a C application which has already crashed without debugging/logging
is what I meant. Assuming you don't add any specific debugging or exception
handling code, in dynamic languages you're guaranteed to get at least a
vaguely helpful exception message, and you will get one (for varying
definitions of "helpful") sometimes in C++, too, if it did not segfault. You
do not get that in C by default.

Sorry for the lack of clarity, my statement was incorrect as written.

~~~
AnimalMuppet
If you meant that and didn't state it perfectly, then I was too harsh. A
mistaken idea may deserve a sharp reply; an incorrectly stated point does not.

------
sanatgersappa
Elm has literally changed the way I think about programming. Can't wait to see
what the future will bring.

~~~
chubot
Can you give an example of a nice Elm program (maybe 50 lines or less) that
illustrates this? I haven't tried Elm, but I'm guessing it has to do with
state management, which IMO is indeed the hardest part of programming.

~~~
sanatgersappa
Sure. Check out the elm version of todomvc [https://github.com/evancz/elm-
todomvc/blob/master/README.md](https://github.com/evancz/elm-
todomvc/blob/master/README.md). Other examples here [http://elm-
lang.org/examples](http://elm-lang.org/examples). Also see
[https://github.com/evancz/elm-architecture-
tutorial/](https://github.com/evancz/elm-architecture-tutorial/) for the
recommended architecture.

~~~
david-given
Will there ever be an Elm which compiles to real machine code? I'm currently
learning Haskell, and find it a combination of mindblowingly amazing (pattern
matching, monads, STM) and mindnumbingly crazy-making (namespaces, debugging,
strictness). Elm looks like a different more modern take on the same
principle. But I don't want to have to run everything inside a Javascript VM.

~~~
wheatBread
I think so! I'd like to get Elm running way faster than JS in browsers (which
is possible thanks to some design choices) and that'd involve getting all this
together. That opens things up on lots of different platforms, including
servers. I also expect the next release to make things nicer on node.js as a
first step in this direction. Point is, I think we'll start seeing folks doing
server stuff, and it's a goal of mine to generate machine code for lots of
reasons! It's a big project so I'm not setting a timeline at this point
though.

Also, thanks for taking a look at Elm! I think of it as a member of "the ML-
family" of languages and I draw from a lot of lessons from working with these
tools and seeing what issues have come up for other folks, both within the
typed functional world and not.

~~~
corysama
I'd be very happy with Elm compiled to WebAssembly as a stepping stone on the
way to fully native :)

~~~
wheatBread
You need a garbage collector either way, so doing one implies you have pretty
much succeeded at the other. So it's like two stepping stones that are right
next to each other and quite a long jump away :P I think we'll get there
though!

------
bhauer
Terrific work!

I would be delighted if the same level of developer-friendliness were present
in other pieces of our toolchain, most importantly in our version control
systems. I routinely feel that while we have version control data structures
suitable for the 2010s, the user interfaces on version control systems are
about two decades behind where they should be.

~~~
eru
What would you like to see changed?

~~~
mgold
I'll opine: git's sense of semantics seems to rival JavaScript's ("sure I'll
add an int and a string"). I checkout branches but also checkout files to undo
unstaged changes, but staged changes are a reset. How about "undo" as a word
people would understand? Nope, doesn't do anything. Can I get a log function
that shows the commit history by default? And when I check out a branch it
says I'm up to date with origin/branchname but it's lying because that's just
the local copy and it hasn't done a fetch. And of course in 2.0 they didn't
try to clean anything up, they just changed a few defaults.

I think I deserve a medal for understanding enough git to get by.
Specifically, a purple heart.

~~~
zevyoura
Try Mercurial.

------
rtfeldman
For anyone in SF, my employer is hosting an Elm meetup next Wednesday night
centered around this release! [http://www.meetup.com/Elm-user-group-
SF/events/223500480/](http://www.meetup.com/Elm-user-group-
SF/events/223500480/)

------
afarrell
Sadly, to this is not as common because in order for it to happen, the idea of
focusing on new-user friendliness needs to be baked into the culture of a team
building a tool. Otherwise, improvements get held up as being superficial,
especially when there is an existing user base who has already gotten used to
how things work that the maintainers can point to and say "see, things work
fine. We should not change things because that could introduce errors and the
existing codebase is already tested."

Automated testing helps implement these things too.

------
cosmicexplorer
Is there an option to make it print the file:line:column? Cause I use emacs
and rely on the compiler printing out error messages in that standard format
so I can jump to them easily; I could certainly hack together a solution for
other output formats, but it's a tad more annoying. Not sure what kind of
users would be complaining about having that and it seems like stronger editor
support for standard compiler output would fix some (definitely not all) of
the problems you identify and solve in this post.

~~~
vilterp
`elm-make --report=json MyFile.elm` gives the error messages as JSON; very
easy to parse out line & col. It's how the vim plugin that there's a video of
in the post was made.

~~~
aidenn0
Yes, but most text-editors can already parse out the format that the gnu
compilers emit, so why not do that?

------
halosghost
Elm has interested me for a while now; particularly as I get more and more
into Haskell (one of the languages that inspired Elm). While I generally find
Haskell's error messages to actually be quite helpful, there are certainly
times where they could be a little more useful.

Well done, so far! I hope this kind of effort is eventually undertaken by many
compilers (and their authors), as everyone benefits from simplifying the
debugging/refactoring process.

~~~
dllthomas
Haskell error messages have come a long way. These days, they're generally
(not quite uniformly) helpful and specific, at the cost of being a bit
verbose.

~~~
AnimalMuppet
I can eyeball-grep the relevant parts of a verbose message, if it actually has
useful information in it. That is, I'll take verbose-but-specific-and-helpful
over terse-but-not-useful-information any day.

~~~
dllthomas
I agree, for sure, on balance. Terse and equally useful is of course ideal,
but often impossible.

That said, "eyeball-grepping" a specific set of messages, along with which
bits are important, is a learned skill. I have found myself many times trying
to fix the wrong thing because I skimmed and wound up with a wrong
understanding of what the error was. And then found a more careful read told
me precisely what was needed.

------
iamwil
I tried making an application in Elm last summer, and the hardest part of the
entire experience was the error messages! I'm glad that they've went ahead and
fixed the problem. I'll be trying it out again soon.

~~~
k__
Yes. This, plus the outdated tutorials.

First the code doesn't work like described and than the error messages don't
help you to navigate around those problems.

------
c3d
I remember changing a compiler message to HP aC++ to quote the C++ standard
verbatim, because at the time aCC was the only compiler doing template
dependent name lookup according to the standard, so we kept receiving bug
reports.

Moral of the story: sometimes, explaining why you are doing something may
require hyperlinks, just saying it's wrong is not convincing enough.

The error messages in XL and Tao3D are terrible. I'm ashamed :-)

------
krick
Nice, but it feels quite unnatural that compiler tries to speak to you like a
human being ("As I infer types of values flowing through your program…"). I
cannot say why I'm against it, or even _if_ I'm against it, but I remember
some postgresql error message guidelines where it was explicitly said not to
do so, cannot remember why though. That's peculiar.

~~~
rtfeldman
Personally I like it. :) Is there a specific reason that makes you prefer an
impersonal compiler?

~~~
Q6T46nT668w6i3m
> Is there a specific reason that makes you prefer an impersonal compiler?

It complicates parsing

~~~
buttproblem
I agree both in parsing via another program as well as parsing error messages
in my brain (not sure which you were intending).

Perhaps it is because compiler messages suck but my brain has been trained to
quickly pick out the line numbers and file names.

------
Touche
> Folks who prefer dynamicly-typed languages are generally of the opinion that
> working with compiler error messages sucks.

This is not at all why I prefer dynamically typed languages. Try doing some
JSON parsing in Haskell for a non-trivial payload (like something with nested
objects) without wanting to pull your hair out.

~~~
rtfeldman
It doesn't have to be like that just because the language is compiled,
though...for example, Elm's JSON parsing is quite nice: [http://elm-
lang.org/blog/announce/0.14#making-json-easier](http://elm-
lang.org/blog/announce/0.14#making-json-easier)

~~~
Touche
I can't tell from those examples if it's good or not, the json shown is all
trivial 1 level deep json that you rarely encounter in real life. How it
handles nested objects and array, deeply nested objects and arrays is what I'm
interested in. Not the ability to handle a Point payload. I know in dynamic
language it's typically a 1 liner. I'll endure a little more work than that,
but my experience from Haskell was quite painful.

------
ninjakeyboard
I feel like the languages I frequently use (scala) don't have usability
issues. This looks nice but about the same.

------
Ciantic
I like elm, but it would be nice to have a more practical elements in it. I
have a problem with elm-html syntax: ` div [ class "profile" ] [ img [ src
user.picture ] [], span [] [ text user.name ] ]`

Even indented, it looks unreadable in elm docs, especially for beginner.

