[Haskell-cafe] Re: Error message reform

Claus Reinke claus.reinke at talk21.com
Mon Jun 1 14:49:44 EDT 2009

> It's too wordy, but it's a start. This is also prime ground for wanting 
> to have configurable levels of error reports, since some users will find 
> it helpful to see both types but others will find it confusing.

Indeed. For this simple example, I find Hugs' message nearly optimal,
but as one could just as well construct examples where GHC's message 
was nearly optimal, I've tried instead to extend the example, until neither 
Hugs nor GHC gives an optimal message. Even so, it is worth comparing 
the two, and their complementary approaches to the problem:

t = (let q=0 in(\y->(\x->(\z->let r=1 in x z))) ()) :: (a->b)->(a->a)

    Couldn't match expected type `a' against inferred type `b'
      `a' is a rigid type variable bound by
          an expression type signature
            at D:\home\Haskell\tmp\desktop\errormessages.hs:2:56
      `b' is a rigid type variable bound by
          an expression type signature
            at D:\home\Haskell\tmp\desktop\errormessages.hs:2:59
    In the expression: x z
    In the expression: let r = 1 in x z
    In the expression: (\ z -> let r = 1 in x z)

ERROR file:.\errormessages.hs:2 - Inferred type is not general enough
*** Expression : let {...} in (\y -> \x -> \z -> let {...} in x z) ()
*** Expected type : (a -> b) -> a -> a
*** Inferred type : (a -> a) -> a -> a

GHC delivers its messages from in-the-middle of its typing process,
which one hopes might give as little information as neccessary to
identify the issue, without irrelevant context. It then tries to explain
that information nicely, followed by some value-level context.

Hugs delivers its messages from on-the-outside-looking-in, which one
hopes might give the maximum potentially relevant information. It makes
no attempt to cushion the blow.

In practice, neither approach works all the time, and even for this
tiny contrived example, one would have to engage in some "type
debugging" (inserting type signatures, to trigger type-level printfs;
commenting out large parts of code to see whether they contribute
to the issue; ..) to figure it out.

Note that both approaches are fairly precise in locating the error
at the type level - it is the cross-reference from type to value level
that runs into trouble, especially if the former is only implicit in the
latter (which is why type debugging by adding signatures helps).

> For really intricate type hacking, even this isn't enough because the 
> programming errors often happen far from where the type errors are 
> finally caught. In an ideal world, ghc could dump the entire proof 
> forest generated by inference, so an external tool[1] could be used to 
> browse through it and track the complete history of where inferred types 
> came from. This gives a partial view of the inferred types for a 
> compilation unit, something I've often wanted (rather than the manual 
> comment/reload/uncomment routine). The proof forest could even be used 
> as an interlingua over which people can write filters to generate 
> messages they find most helpful.

Yes, most research papers on this ended up suggesting visual representations
and separation between generating and presenting/querying error information.

> Ah the joys of ideal worlds... ;)

"if you don't have a dream, how you gonna have a dream come true?" (Bloody Mary)


More information about the Haskell-Cafe mailing list