Opened 5 years ago

Closed 5 years ago

#9612 closed feature request (fixed)

Use functional dependencies to give more specific error messages

Reported by: rwbarton Owned by:
Priority: normal Milestone:
Component: Compiler (Type checker) Version: 7.8.3
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: typecheck/should_fail/T9612
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


An example from

f ::(Eq a) => a -> (Int, a) -> Writer [(Int, a)] (Int, a)
f y (n,x) | y == x    = return (n+1, x)
          | otherwise = do tell (n,x)
                           return (1,y)
    Could not deduce (MonadWriter
                        (Int, a) (WriterT [(Int, a)] Data.Functor.Identity.Identity))
      arising from a use of ‘tell’
    from the context (Eq a)
      bound by the type signature for
                 f :: Eq a => a -> (Int, a) -> Writer [(Int, a)] (Int, a)
      at 180.hs:42:5-57
    In a stmt of a 'do' block: tell (n, x)
    In the expression:
      do { tell (n, x);
           return (1, y) }
    In an equation for ‘f’:
        f y (n, x)
          | y == x = return (n + 1, x)
          | otherwise
          = do { tell (n, x);
                 return (1, y) }

GHC could realize that the class MonadWriter m w has a functional dependency w -> m and notice that there is an instance MonadWriter w (WriterT w m1) whose m-part unifies with that of the needed constraint. Therefore the error cannot be a missing instance, and GHC could give a more friendly error message like

    Couldn't match expected type `[(Int, a)]'
        with actual type `(Int, a)'
    when unifying the instance `MonadWriter w (WriterT w m1)'
    with the constraint `MonadWriter (Int, a) (WriterT [(Int, a)] Identity)'
    arising from a use of 'tell'
    In a stmt of a 'do' block: tell (n, x)
    In the expression:

(Or, if necessary, Could not deduce [(Int, a)] ~ (Int, a) from the context (Eq a) ...)

Change History (5)

comment:1 Changed 5 years ago by simonpj

Which version of GHC are you using? With HEAD or 7.8 I get

    Couldn't match expected type ‘[(Int, a)]’
                with actual type ‘(Int, a)’
    Relevant bindings include
      x :: a (bound at T9612.hs:5:8)
      y :: a (bound at T9612.hs:5:3)
      f :: a -> (Int, a) -> Writer [(Int, a)] (Int, a)
        (bound at T9612.hs:5:1)
    In the first argument of ‘tell’, namely ‘(n, x)’
    In a stmt of a 'do' block: tell (n, x)

which is certainly better than the one you display.

I had to add import Control.Monad.Trans.Writer.Strict at the top.


comment:2 Changed 5 years ago by rwbarton

Oh, I meant for tell to be the one from mtl Control.Monad.Writer, sorry.

class (Monoid w, Monad m) => MonadWriter w m | m -> w where
    writer :: (a,w) -> m a
    tell   :: w -> m ()
    listen :: m a -> m (a, w)
    pass   :: m (a, w -> w) -> m a

comment:3 Changed 5 years ago by Simon Peyton Jones <simonpj@…>

In 20632d37b5bcb68bb0ca34238f1ed49c7be3a8f7/ghc:

Do not discard insoluble Derived constraints

This is preparing for a fix to Trac #9612. The idea is that insoluble
constraints are nice solid errors that we should not discard before
we have a chance to report them.  So TcRnTypes.dropDerivedWC now
keeps insoluble Derived constrains, and instead TcSimplify.solve_wanteds
filters them out

We get somewhat better error message for kind-equality failures too.

A slight downside is that to avoid *duplicate* kind-equality failures
when we float a kind-incompatible equality (e.g.  alpha:* ~ Int#),
I've disabled constraint-floating when there are insolubles.  But that
in turn makes a handful of error messages a little less informative;
good examples are mc21, mc22, mc25.  But I am re-jigging the
constraint floating machinery in another branch, which will make this
go back to the way it was before.

comment:4 Changed 5 years ago by Simon Peyton Jones <simonpj@…>

In 1a88f9a4fb373ce52284996212fc23b06848b1c0/ghc:

Improve error messages from functional dependencies

Reponding to Trac #9612:

 * Track the CtOrigin of a Derived equality, arising from a
   functional dependency

 * And report it clearly in the error stream

This relies on a previous commit, in which I stop dropping Derived
insolubles on the floor.

comment:5 Changed 5 years ago by simonpj

Resolution: fixed
Status: newclosed
Test Case: typecheck/should_fail/T9612

Good idea, thank you. The error message is now

    Couldn't match type ‘[(Int, a)]’ with ‘(Int, a)’
    arising from a functional dependency between:
      constraint ‘MonadWriter (Int, a) (WriterT [(Int, a)] Identity)’
        arising from a use of ‘tell’
      instance ‘MonadWriter w (WriterT w m)’ at T9612.hs:20:10-59


Note: See TracTickets for help on using tickets.