[Haskell-cafe] Re: Error message reform

wren ng thornton wren at freegeek.org
Sun May 31 18:02:37 EDT 2009


Tillmann Rendel wrote:
> wren ng thornton wrote:
> > (Though it doesn't necessarily generalize to cover similar messages like:
> >
> >     Prelude> :t (\x -> x) :: a -> b
> >     <interactive>:1:7:
> >         Couldn't match expected type `b' against inferred type `a'
> >           `b' is a rigid type variable bound by
> >           the polymorphic type `forall a b. a -> b' at <interactive>:1:0
> >           `a' is a rigid type variable bound by
> >           the polymorphic type `forall a b. a -> b' at <interactive>:1:0
> >         In the expression: x
> 
> I find this slightly more complicated case quite confusing with the 
> current wording:
> 
>   Prelude> :t (\x -> x) :: (a -> b) -> (a -> a)
>   <interactive>:1:7:
>       Couldn't match expected type `a' against inferred type `b'
>         `a' is a rigid type variable bound by
>             an expression type signature at <interactive>:1:14
>         `b' is a rigid type variable bound by
>             an expression type signature at <interactive>:1:19
>       In the expression: x
>       In the expression: (\ x -> x) :: (a -> b) -> (a -> a)
> 
> This message suggests that ghc has inferred type b for x.


I agree with Claus that this (both of these, actually) are symptoms of 
not giving enough type information for the user to reconstruct what the 
problem actually is.

I think an initial solution should be to print out _some_ type inferred 
for x. We can't print out "the" type, because the error is that there 
isn't one. As for which type to print for x, it'd be most informative 
(though perhaps not most helpful) to print both types that are being 
unified, and to alter the rigidity messages to indicate which type 
they're rigid for. For example, perhaps something like this:

     Couldn't match expected type `a' against inferred type `b'
     In the expression: x
         which should have type: (a -> a)
             where `a' is a rigid type variable bound by
                 an expression with type signature at <interactive>:1:14
         but actually has type: (a -> b)
             where `b' is a rigid type variable bound by
                 an expression with type signature at <interactive>:1:19
     In the expression: (\ x -> x) :: (a -> b) -> (a -> a)

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.


Sometimes, even when the complete type is given, that's not enough 
information because each of the type variables are non-linear and so 
it's not clear where exactly the problem crept up. We can see a little 
of this in the above example where, even with the new message, `a' 
occurs twice in the first type. By presenting both types, users can 
figure it out here--- though with only the last type signature given it 
wouldn't be clear whether it wants ((b->b)->a->a) or ((a->b)->a->b) or 
even ((a->b)->b->a).

In more complex examples I've often found non-linearity to be the most 
uninformative part of the current messages. Perhaps a message like above 
which gives both types will be enough in practice to clear it up. If 
not, then it may be worth providing some sort of metasyntax to 
distinguish what subpart of the type is being discussed (e.g. bold face, 
or an underlining carrot on the next line, etc).

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.

Ah the joys of ideal worlds... ;)


[1] Something like the Dynasty[2] debugger for logic programming in Dyna.

[2] http://www.cs.jhu.edu/~jason/papers/eisner+al.infovis06-poster.pdf

-- 
Live well,
~wren


More information about the Haskell-Cafe mailing list