Skip to content

The hint to add missing context to the type signature is not produced by GHC when using non-trivial constraint

Intro

When a user didn't specify typeclass constraints for all variables in a function signature the GHC sometimes suggests to add relevant constraint to the signature, like this:

    • No instance for (Foo a) arising from a use of ‘foo’
      Possible fix:
        add (Foo a) to the context of
          the type signature for:
            bar :: forall a. a -> Sum a

However if the constraint is a "non-trivial" one, in particular if it uses multiparameter typeclass and some of the parameters not variables then the suggestion is not produced. My expectation is that suggestion should be produced.

A bit of context, the Haskell Language Server uses GHC's error messages to suggest fixes. When error message contains the Possible fix similar to the one shows before then in can add the constraint to the type signature automatically as a code action. But if the suggestion is missing then it's just not possible. It appears that only GHC has all the necessary info regarding whether to produce Possible fix or not so the issue cannot be worked around in HLS (nor should it be since GHC's error messages are useful even for users that don't use HLS).

Specific cases

For the following program the suggestion is not produced:

{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Kind
import Data.Monoid

class Foo (a :: Type) (b :: Type) where
  foo :: Monoid m => (a -> m) -> b -> m

bar :: a -> Sum Int
bar x = foo Sum x
/tmp/test2.hs:10:9: error:
    • No instance for (Foo Int a) arising from a use of ‘foo’
    • In the expression: foo Sum x
      In an equation for ‘bar’: bar x = foo Sum x
   |
10 | bar x = foo Sum x
   |         ^^^

But for a slightly simpler program the suggestion is present

{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Kind
import Data.Monoid

class Foo (a :: Type) where
  foo :: Monoid m => (a -> m) -> a -> m

bar :: a -> Sum a
bar x = foo Sum x
/tmp/test2.hs:10:9: error:
    • No instance for (Foo a) arising from a use of ‘foo’
      Possible fix:
        add (Foo a) to the context of
          the type signature for:
            bar :: forall a. a -> Sum a
    • In the expression: foo Sum x
      In an equation for ‘bar’: bar x = foo Sum x
   |
10 | bar x = foo Sum x
   |         ^^^

A bit of investigation

I found out that call to isTyVarClassPred at https://gitlab.haskell.org/ghc/ghc/-/blob/master/compiler/GHC/Tc/Errors/Ppr.hs#L2844 is responsible for GHC's decision to omit the suggestion. The call appears to check that typeclass is only applied to variables and thus fails for a constraint like Foo Int a which has constants mixed in.

Perhaps the check should be relaxed to require that constraint to be suggested has some variables in its parameters? Or maybe the check shouldn't be performed at all (not really sure about this one though)?

One thing to probably watch out for is that the variable can occur as part of some other type in the constraint as illustrated by following modification to the program

{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Kind
import Data.Monoid

class Foo (a :: Type) (b :: Type) where
  foo :: Monoid m => (a -> m) -> b -> m

newtype Quux a = Quux { unQuux :: a }

bar :: a -> Sum Int
bar x = foo Sum (Quux x)
/tmp/test2.hs:12:9: error:
    • No instance for (Foo Int (Quux a)) arising from a use of ‘foo’
    • In the expression: foo Sum (Quux x)
      In an equation for ‘bar’: bar x = foo Sum (Quux x)
   |
12 | bar x = foo Sum (Quux x)
   |         ^^^

NB if I just omit the call to isTyVarClassPred the patched ghc produces the error message I was expecting all along:


    • No instance for (Foo Int (Quux a)) arising from a use of ‘foo’
      Possible fix:
        add (Foo Int (Quux a)) to the context of
          the type signature for:
            bar :: forall a. a -> Sum Int
    • In the expression: foo Sum (Quux x)
      In an equation for ‘bar’: bar x = foo Sum (Quux x)
   |
12 | bar x = foo Sum (Quux x)
   |         ^^^

Environment

  • GHC version used: 9.2.2, HEAD

Optional:

  • Operating System: Linux
  • System Architecture: x86_64
Edited by Sergey Vinokurov
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information