On the Ergonomics of FP compiler errors in PureScript / Haskell

Authors of compilers for Pure Functional Programming languages with as rich, extensive and extensible type-systems as the current-gen "ML family" (top-spots Haskell, Elm, PureScript) seem to find it challenging to formulate many-if-not-most of their compiler's error messages —typically arising in already highly abstracted, über-generalized "meta-sphere" contexts— in a manner that won't frustrate practically all beginner and intermediate users, plus a substantial number of veterans. As a result, complaints about "cryptic errors" abound and often top the lists-of-criticisms in most threads about Haskell and PureScript.

Now Elm is famed for it's user-friendly error messages, and I have good reason to believe that a lot of thought (hence, a lot of time) went into them — though the simple reality of course is that their language doesn't offer any powers "do express cryptic things" in the first place, which greatly simplifies its formulation of error messages! (Still quite the effort I'm sure — I know from experience how it's "the little things" that eat up your time yet make all the difference in the product to most (prospective) users.)

My simple hack for writing non-irritating error messages

without spending hours pondering over them, would be to ditch most FP compiler authors' apparent implicit assumption that their users would surely like to fully understand or even study more about the intricate conundrum their (usually) type-checker / type-inferencer got unresolvably stuck in at this moment. Not so! At least, not here, not right now. Not when this error shows up. Compilers: save the elaborate polite deductions. To illustrate my thinking here, I posted the following in a recent thread on the topics, which was wordy enough to repost here. To recap:


jmite writes:

"Rigid/skolem type variable bound by x has escaped" — In all seriousness (...) Elm puts a ton of effort into error messages and keeping the language minimal.

Phil A. Freeman responds:

"Our error messages have gotten quite a lot better since then. In that case, you would see something like:

  The type variable h, bound at

    File.purs, line 9, column 14 - line 9, column 25

  has escaped its scope, appearing in the type

    Eff t1 (STRef h0 Int)

in the expression runST (newSTRef 1)
in value declaration test

which I think is pretty decent, given what's actually going on in this case. As you add more type system features, of course it will be harder to understand the ways in which it can fail. It's one of the reasons why Elm is better for newcomers to FP. I would love to see a language with Elm's error messages and PureScript's type system features, I just don't know if it's possible."

My take:

Definitely an improvement. If you follow through on the guiding principles behind these intuitive changes, I think it's straightforward in the overwhelming majority of such cases to easily and quickly arrive at a final ultimate form that highlights the problematic part in the code, not why it bothers the compiler. This makes things more "actionable". For example in the above, what bothers the compiler is "escaped its scope", but for anyone who didn't ever implement a type-system as rich as PureScript's, they don't care about what they don't necessarily currently, right-this-minute, while iterating-and-about-to-ship, grok. What they do care about here is "h appears in Eff t1 etc and this was unexpected and I, purs can't deal with that". So to redesign such error messages would be akin to:

You cannot refer to type variable h..

    File.purs, line 9, column 14 - line 9, column 25

..out of scope in type:

    Eff t1 (STRef h0 Int)

in expression runST (newSTRef 1)
in declaration 'test'

This looks like very, very minor changes (a fair number of them if you compare closely), even has a few more words, but I think all of them together quite reduce mental load and "parsing effort" for the (Pure)scripter "in the heat of the moment". Less detail and prose here means more instant identification of the offender to be attacked:

It's not about doing ELI5 messages, simply cutting all jargon and verbiage to not further irritate. A compiler can never be argued with, so it doesn't need to explain it's full reasoning and justifications in deep detail and it's better ergonomically (in this situation which is a dev session, not a lecture or tutorial) for it to just calmly assert, like a 1950's officer puffing a Cohiba with a semi-grin, that "you cannot do X, simple as that", which is (well, should be) the essence of 99% of compiler messages. Any details, there's a link included already. As if a type-variable developed a mind of its own and "escaped"! Very colourful I'm sure in type-theory papers or conference talks, but just an unnecessary roadblock to "the hacker on the ground" and while the air and mind are filled thick with anxious impatience to see one's latest changes built "stat".