Which error message do you find easier to read? - Haskell - kbin.social

2 Likes

Ooh, I can do a poll here too:

  • GHC-style
  • Elm-style

0 voters

1 Like

Elm errors are a nightmare to work with. It might be nice when you are a total beginner and have only one error but it becomes quiclly overhelming and vi unfriendly, the same error appers as 10 different errors.

2 Likes

I’m in particular looking for feedback on the error style and formatting, not so much the amount of errors or the content of the errors.

1 Like

I don’t really like either, but I slightly prefer GHC-style.

I like all the relevant location information in one place so I don’t have to look around, so splitting the filename and line number into two different places isn’t something I’m keen on.

I’d prefer having the offending line(s) first, followed by what sort of error it is, and then followed by other relevant details, but of the two options given, I prefer Elm’s approach where it’s immediately below the line stating what kind of error it is, instead of having the offending line at the bottom of the message.

I’m not a fan of the extraneous vertical whitespace in the Elm style error message - bullet points work well enough to group things and allow me to fit more errors on my screen. More whitespace means more scrolling when there are several errors.

I find that the in the ... of are almost always useless, especially when several functions are chained together with something like (.). I am in 100% agreement they should be removed. For example, I was just given

src/Optken.hs:56:54: error: [GHC-83865]
    • Couldn't match type: Endo (Map Text Bool)
                     with: Map k0 a0 -> Map Text Bool
      Expected: OptionParser err a
                -> Const (Map k0 a0 -> Map Text Bool) a
        Actual: OptionParser err a -> Const (Endo (Map Text Bool)) a
    • In the second argument of ‘(.)’, namely ‘go’
      In the first argument of ‘btraverse’, namely ‘(fmap I . go)’
      In the second argument of ‘(.)’, namely ‘btraverse (fmap I . go)’
   |
56 | flags = ($ M.empty) . getConst . btraverse (fmap I . go)
   |                                                      ^^

and my error was really that I wanted (`appEndo` M.empty) instead of ($ M.empty). Three steps seems completely arbitrary, wastes 3 lines and often doesn’t reach the problem anyway.

2 Likes

I am not talking about the amount of errors.
I am saying that elm when showing a multiple line error (because for example the expression is written over 10 lines), it will list the 10 lines (fine) but each with its own line number which is seen by most text editor as 10 different errors (not good).
Also errors are gregerious animals and it is importatnt to be able separate them visually. GHC with a clear file:line error type at the beginning is clearer in my opinion.
Finally, tool friendly is as important as user friendly if not more (the user is easier to train than the tool, until now).

It’s 2023, tools should just use -ddump-json (and issues like #19278: -ddump-json is underspecified · Issues · Glasgow Haskell Compiler / GHC · GitLab should be fixed asap and it seems @benbellick is doing just that).

1 Like

You should definitely also take a look at how Helium outputs error messages. From all the systems I have seen so far, I very much prefer the way they output error messages. (Helium is a bit difficult to install, see instructions below).
The message for a simple type error mismatch looks like this:

Compiling Foo.hs
(10,7): Type error in application
 expression       : f i
 function         : f
   type           : Bool -> String
 1st argument     : i
   type           : Int 
   does not match : Bool

You should definitely try out some examples using Helium. They also implement a lot of special cases for common errors.

How to install Helium

Helium is a bit tricky to get running, for two reasons: It uses an additional compilation step which compiles attribute grammars to Haskell code, and it hasn’t been updated for a while. I have a fork where I comitted the Haskell files generated from the attribute grammars with uuagc (the Utrecht attribute grammar compiler), and made the code compatible with GHC 9.2.5:

You should be able to just run cabal install helium from that fork if you use GHC 9.2.5.

3 Likes

Agreed that the “in the expression” information is not really something I’ve ever looked at. An important exception is generated code with no location info, e.g.

singletons [d|  data Foo :: Type -> Type where  |]

produces the error

Singletons.hs:82:1: error:
    • Unexpected kind variable ‘a_a911’
      Perhaps you intended to use PolyKinds
    • In the data type declaration for ‘SFoo’
   |
82 | singletons [d|
   | ^^^^^^^^^^^^^^...

But even here I think perhaps a better approach instead of “In the data type declaration” would be Having something like

Singletons.hs:82:1: error:
    • Unexpected kind variable ‘a_a911’
      Perhaps you intended to use PolyKinds
   |
82 | singletons [d|
   | ^^^^^^^^^^^^^^...
    • In the generated code
   |
   | data SFoo :: forall (a_a911 :: Type). Foo_a910 a_a911 -> Type
   |                                                ^^^^^^
3 Likes