[racket-dev] Error phrasing (error-message overhaul)

From: Eli Barzilay (eli at barzilay.org)
Date: Tue Jun 19 15:52:19 EDT 2012

About a month ago, Matthew Flatt wrote:
> I've pushed a first cut at overhauling error messages from
> `racket/base'.

Two very independent things really bug me, so I'll make two posts.

The new messages look more organized but there was something that
bugged me which took a while to realize.  I know that there was some
discussion on this, and even though I've seen it a good number of
times now, I still find this error extremely to read:

    > (map car)
    application: no clause matching given number of arguments
      procedure: map
      given number of arguments: 1
      arguments:
       #<procedure:car>

(Sidenote: there's a missing "expected" in here.)

Regardless of the meta-point of whether it's the right thing to have
the error come from `map' or from `application', the bottom line when
it comes to me debugging code is that for all errors I look for the
first "xxx:" which in the majority of cases is enough information to
know what broke, and only later I look for source location and
possibly the stacktrace.

So having the above error is extremely disruptive to me -- every time
that it happened I had to force myself to explicitly read all of the
lines slowly otherwise I always end up missing the name.

Another place where this is a problem is undefined identifiers (and
now that I think about it I realized that in this case the annoyance
is not new):

    > blah
    reference to undefined identifier
      identifier: blah
      context:
        [...]
    > (module foo racket blah)
    expand: unbound identifier in module
      in: blah
      source:
       stdin::32
      context:
       /home/eli/src/plt/collects/racket/private/misc.rkt:87:7

In both of these messages it's hard to immediately find the `blah' --
worse if I'm unaware of what "expand" is.

I therefore think that it would be *way* better if that first `x:' is
reserved for the short name, if any, that is most likely to point me
in the general direction of "the thing that went wrong".

In addition, it would be very nice if the line that followes the first
`x:' starts with the meat of the problem, so "unbound identifier in
module" is good, "reference to undefined identifier" is slightly
worse, and "no clause matching given number of arguments" is very bad
(I need to know what a "clause" is, I see that there's some kind of
matching involved which completely confuses newbie-me, and there's a
"given number of arguments" which is indirectly related to the code I
wrote).

So an attempt to improve these errors with the above (and no attempt
at dealing with language subtleties) gets me to something like:

> (map car)
map: wrong number of arguments
  expected: 2 arguments or more
  given: 1 argument
  arguments: #<procedure:car>

Notes:
- I also shuffled the given/expected, since "given number of
  arguments: 2" is again making me read a whole bunch of words instead
  of emphasizing the important "given: 2" first, with the "2 what"
  addition following that.
- In the current error message there's a weird difference between
  "map" and "#<procedure:car>" in a way that looks again very
  confusing.  In this format, it's clear (as it was) why the first is
  shown as just a name.

> (module foo racket blah)
blah: unbound identifier in module
  location: stdin::123
  [...same...]

Notes:
- Dropped "expand" since it's irrelevant, for both newbies (who don't
  know what that is), and for pros (who know that this is where all
  syntax errors come from).
- Used "location" which looks clearer for its purpose compared to
  "source".
- And put the location on the same line, which makes it easier to read
  and I think it's worth the ~5 character price.  (Alternatively, long
  source locations can be split onto following lines.)

> blah
blah: undefined identifier in toplevel
  [...same...]

Same as the above, with a minor tweak.

> (+ 1 'a)
+: bad arguments
  [...same...]

In here the "bad arguments" is very easy to digest -- vs the current
"contract infringement" that are much more sophisticated and hard to
read.  (And if I read this email, I'd never see the typo I just did
exactly because of this.)

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the dev mailing list.