[HN Gopher] Compiler Errors for Humans (2015) ___________________________________________________________________ Compiler Errors for Humans (2015) Author : todsacerdoti Score : 67 points Date : 2022-11-24 14:29 UTC (8 hours ago) (HTM) web link (elm-lang.org) (TXT) w3m dump (elm-lang.org) | shadowofneptune wrote: | I'm reminded of these error messages from an Apple C compiler: | | https://www.cs.cmu.edu/~jasonh/personal/humor/compile.html | | - "String literal too long (I let you have 512 characters, that's | 3 more than ANSI said I should)" | | - "...And the lord said, 'lo, there shall only be case or default | labels inside a switch statement'" | | - "You can't modify a constant, float upstream, win an argument | with the IRS, or satisfy this compiler" | | Some of them are more helpful than others, but they are all | rather human. | mannyv wrote: | Too many errors on one line, make fewer. | LoganDark wrote: | I don't regret reading this comment _before_ clicking the | link | mannyv wrote: | There was one that went something like "a typedef (or | something) at this point was a complete surprise to me.' | capableweb wrote: | Better than the infamous PHP error message made in Hebrew: | T_PAAMAYIM_NEKUDOTAYIM | kibwen wrote: | Elm's error messages were the explicit inspiration for Rust's | current error messages: https://blog.rust- | lang.org/2016/08/10/Shape-of-errors-to-com... | convolvatron wrote: | everyone opines about lovely rust error messages are. but in | there is 'you really need to add lifetime annotations'. advice, | which is followed, will result in days of mad refactoring, only | to discover that was a _really bad plan_. | | pure evil | dmitriid wrote: | I think they were an inspiration for most modern languages. | Either directly or indirectly. | AlbertCory wrote: | Favorite experience with compiler errors: C++ in 2004 or so, when | the errors were so voluminous and inscrutable that you had to | install another program to massage them into something readable. | cratermoon wrote: | The first time I tried writing a C++ program with templates | (very early on, like mid-late 90s), the compiler error messages | were longer than the little program I was trying to write. It | put me off on the language and the compiler so much that I went | back to plain C and off into Perl and other languages, and | never really got back to it. | BoppreH wrote: | Should a compiler also suggest applying the fix? | | I'm working on my own toy language and wondering if simple, | obvious errors (like the `map` -> `nap` typo from the article) | should come with an "Apply fix? [y/N]" option when run | interactively. | jamesmunns wrote: | Both Rust (via 'cargo fix') and Clang (via `clang-tidy -fix` | have ways of doing this, I don't think I've seen someone offer | this automatically/interactively though. | | I don't think I've ever used them, but I've ABSOLUTELY used | suggested fixes from LSP/rust-analyzer tools, which fit much | more into my typical workflow. For things like match statements | in Rust (which need to be exhaustive), I now have muscle memory | to just write an empty match statement, and trigger the first | LSP/r-a suggestion, which fills the match block with | placeholder items. | | I guess I see LSP as my "compiler in interactive mode" | interface (even if that isn't EXACTLY true). | masklinn wrote: | Maybe as an opt-in (mode or blanket approval), having to | continue on every compiler error which has a fixer would be | rather frustrating. | | Clippy supports fixes, but will only apply them if `--fix` is | supplied. It does not ask for individual case, the assumptions | likely being that if you're running this opt-in you can | probably do so with a clean working copy and revert whichever | fixes you didn't want or are incorrect. | | An other issue is if you're providing fixers for suggestions, | the fixer has to be extremely reliable, not a 90% thing, | because replacing broken code by possibly subtler broken code | is not great. | warpech wrote: | I collect examples of systems that have helpful error messages. | Here's a blog post (not mine) that praises error messages in | React [1]. Any other examples? | | [1] https://codeburst.io/the-true-delight-of-reacts-error-and- | wa... | seanwilson wrote: | > Folks who prefer dynamically-typed languages are generally of | the opinion that working with compiler error messages sucks. | | I think a big factor here is dynamic languages can show concrete | variable instantiations to help you understand what went wrong so | it's less abstract e.g. the code `plus x y` might give the | runtime error "x was 'abc' which is type String and not type | Number" vs something more abstract and confusing like "x is type | String and not type Number" that you tend to see in compile-time | errors. | | Are there any languages that give concrete variable instantiation | examples as part of compile-time error messages? For instance, | example values could be generated from the types, come from | previous program runs, or supplied by the coder somewhere. | | When errors get tricky, you tend to start plugging in concrete | values or stepping through the program manually so compilers | could definitely help more here. | ygaitonde wrote: | In OCaml, if your pattern match is non-exhaustive, then the | compiler generates example patterns that you aren't accounting | for in addition to showing the missing type. | | Here's an example: | https://stackoverflow.com/questions/22737031/this-pattern-ma... | renox wrote: | I still wonder why editors don't understand line numbers on the | CLI <editor> file:5 | | The editor should try to find if 'file:5' exist and open it if | so, if it doesn't it should try to open 'foo' and then go to line | 5. | | Sounds sensible no? But I don't know any editor which does this. | masklinn wrote: | > The editor should try to find if 'file:5' exist and open it | if so, if it doesn't it should try to open 'foo' and then go to | line 5. | | Opening a file is often done in creation, and `file:5` is a | perfectly valid filename, so it's a bit debatable for a | default. | | OTOH in Emacs you should be able to define a find-file-not- | found-functions hook[0] and implement whatever fallback you | want. I assume `emacs <file>` calls `find-file` after it's | initialised the editor itself. | | [0] | https://www.gnu.org/software/emacs/manual/html_node/elisp/Vi... | renox wrote: | > Opening a file is often done in creation, and `file:5` is a | perfectly valid filename, so it's a bit debatable for a | default | | You're right, but even as an option I think it'd be nice. | euiq wrote: | Visual Studio Code does this (see "File links"): | <https://code.visualstudio.com/docs/terminal/basics#_links> | renox wrote: | I don't think it works from the CLI: code <file>:5 doesn't do | what's expected. | matsemann wrote: | JetBrains products do this for lots of stuff if you use the | built in terminal to launch stuff or their runner. | oftenwrong wrote: | vim does: vim file +5 | renox wrote: | But compiler error messages are file:5 so this isn't good | enough. | Existenceblinks wrote: | I seem to have an unpopular opinion about humanized compiler | error messages. It's better than cryptic messages for sure but I | think it's worse than robotic but concisely clear error messages. | For example, for a type error, don't write mini book for me, just | tell me "line: 56 column: 97, s/int/string/". Same for | complicated error, don't try to be human it's even more | confusing. | cloogshicer wrote: | Out of genuine curiosity: Why do you think that? Trying to | understand where you're coming from. | Existenceblinks wrote: | I wrote Elm before and while the error message is nice it | takes longer to iterate and fix stuff. Because It's in a mix | mode of docs and error message. I only want a clear error | message concisely. | cloogshicer wrote: | Interesting, thanks for explaining. | jdkoeck wrote: | > line: 56 column: 97, s/int/string/ | | Sorry, but that's the least accessible error message I have | ever read. | capableweb wrote: | You haven't dealt with PHP by any chance, have you? | | Not saying the above is good/bad, just saying that there are | (was) worse offenders out there for sure. | Existenceblinks wrote: | Half serious, I think JS' "undefined is not a function" | wouldn't be terrible if it tells me _where_ it lost its | value. The message itself is already ok. | Existenceblinks wrote: | I suspect the downvoter misinterprets my "example". | s/int/string/ meant to be semantics. Something like diff in | test or text diff with semantic help. | dang wrote: | Discussed at the time: | | _Compiler Errors for Humans_ - | https://news.ycombinator.com/item?id=9805978 - June 2015 (90 | comments) | dtgriscom wrote: | In the early '80s at MIT, we learned the CLU [0] language. The | wonderful aspect of that compiler is that when it hit a syntax | error it would suspend compilation for a few dozen lines, or | whatever it took to get past the blast radius of the error, and | then continue compiling, looking for further errors. You'd see | the error message on the faulty line, and then a "... resuming | compilation here" message showing where it would start checking | for more error messages. | | You (obviously) wouldn't get a useable binary, but at least you'd | see more than one error at a time, without the cascading sequence | of errors you see in (most?) current compilers. | | [0]: https://en.wikipedia.org/wiki/CLU_(programming_language) | squaredot wrote: | I would love to have something like this. With Julia, a | programming language that I like very much, it's really a pain. | blindseer wrote: | The first thing I thought when reading this blogpost was "Can I | send this to a Julia core dev? Would they understand how much | nicer error messages are in other ecosystems?" | | I think even basic things like the order of error messages is | all backwards to me. Take this very silly example: | julia> foo() = println(123 * "hello") foo (generic | function with 1 method) julia> bar() = foo() | bar (generic function with 1 method) julia> baz() | = bar() baz (generic function with 1 method) | julia> baz() ERROR: MethodError: no method matching | *(::Int64, ::String) Closest candidates are: | *(::Any, ::Any, ::Any, ::Any...) at operators.jl:591 | *(::T, ::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, | UInt128, UInt16, UInt32, UInt64, UInt8} at int.jl:88 | *(::Union{AbstractChar, AbstractString}, ::Union{AbstractChar, | AbstractString}...) at strings/basic.jl:260 ... | Stacktrace: [1] foo() @ Main ./REPL[3]:1 | [2] bar() @ Main ./REPL[4]:1 [3] baz() | @ Main ./REPL[5]:1 [4] top-level scope @ | REPL[6]:1 | | In Julia the type of the error is printed out immediately at | the point where the error occurs, then a bunch of hints about | closest candidates, then the function and filename where the | error occurred, then the function that called it, then the | function that called that. It's all backwards. In order, it's | | 1. important information 2. arbitrary hint which could be | useless 3. most important (where the error occurred) 4. less | important 5. less less important | | When the stacktrace is long, this is so painful to deal with in | a REPL environment. You almost always have to scroll to find | out information about the error, and you have to scroll just | the right amount, or else ... | | And even the stacktrace arguably doesn't contain all the | information it should, demonstrated well by this blog post how | nice it could be. | | VSCode is preferred way of using Julia (for me and for better | or for worse for everyone). For long stacktraces I have to | first scroll all the way back up to see what is going on. So | often I have a very tiny terminal open at the bottom of my | screen, and it is EXTREMELY annoying to do that. I often scroll | too much and I overshoot, and just finding the error is a | challenge. There's so many times where I'm just struggling to | find the error, and it's just an exercise in frustration to be | honest. | | Here's the same example in Python. In [1]: | def foo(): ...: print("1" + 1) ...: | In [2]: def bar(): ...: foo() ...: | In [3]: def baz(): ...: bar() ...: | In [4]: baz() ----------------------------------------- | ---------------------------------- TypeError | Traceback (most recent call last) Input In [4], in | <cell line: 1>() ----> 1 baz() Input In | [3], in baz() 1 def baz(): ----> 2 | bar() Input In [2], in bar() 1 def | bar(): ----> 2 foo() Input In [1], in | foo() 1 def foo(): ----> 2 print("1" | + 1) TypeError: can only concatenate str (not | "int") to str | | The last line tells me the error. The line before that tells me | where. I can scroll up to find more information if I want to. | | Why doesn't Julia work like this? Who knows. I've made | suggestions on slack and to people in person but I've been met | with disdain for the most part. | | Not to mention that almost ALL my errors are MethodError types. | | And worst of all, Julia doesn't show you the string of your | code in the error, so you have to click on the line that has | the file and hope VSCode opens that file at that line. It's SO | backwards. If you are using neovim / vim / tmux, you have | manually copy paste the line that contains the file name and | line number into a new terminal. Or just navigate to the file | and line number manually. Ugh. In Python, I can see the error | and know what caused the error. In Julia, I see the error, kind | of sort of know what might be the problem, try to find the | exact file and line, check if my assumptions are correct, if | not traverse up the method dispatch call stack and try to | predict what might be going on. | | And I consider myself an experienced Julia developer. For my | team members coming from Rust or Python when they get a error | running code, it's just brutal during the learning process. | I've had people come up to me and say to my face, Julia sucks | and I shouldn't write or advocate it anymore. | | This is barely touching the surface. Error reporting with | macros is even worse. My team has managed to segfault our | program multiple times and we are left completely in the dust | then. | | There's SOOOOO many examples like this where I think usability | in Julia needs to be improved. Sigh. Maybe some day. | eckza wrote: | After shipping several production applications in Elm over the | last five years... I can confirm that the development experience | is an absolute joy. | | With a compiler this helpful, and sub-second builds-on-save - you | get really tight feedback loops. | | I can't say enough good of it. | capableweb wrote: | If you enjoy tight feedback loops and want something even | quicker than "rebuild on save", you should try something like | Clojure and hook up your editor to a REPL running in the | background. Rather than "recompile the whole program on save", | you can send small snippets to be evaluated in the live | environment, updating your program on the fly :) | tmtvl wrote: | I was fairly happy doing REPL development with Scheme, and | then I decided to give Common Lisp a try and I discovered | what REAL REPL-driven development is. | | The first time I changed some data representation from a | defclass to a defstruct and SBCL asked me what I wanted to do | with existing instances of the type was mindblowing. | hsuduebc2 wrote: | I sometimes remember horrors of "undefined index of null" which | are for new developers quite frustrating. Good job! ___________________________________________________________________ (page generated 2022-11-24 23:00 UTC)