error-messages icon indicating copy to clipboard operation
error-messages copied to clipboard

Scoped type variables not suggested

Open MorrowM opened this issue 4 years ago • 4 comments

Consider the following file:

module ErrMsg where

f :: a -> a
f x = y
  where
    y :: a
    y = x

When loaded up in GHCi we get the following error message:

ErrMsg.hs:7:9: error:
    • Couldn't match expected type ‘a1’ with actual type ‘a’
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          y :: forall a1. a1
        at ErrMsg.hs:6:5-10
      ‘a’ is a rigid type variable bound by
        the type signature for:
          f :: forall a. a -> a
        at ErrMsg.hs:3:1-11
    • In the expression: x
      In an equation for ‘y’: y = x
      In an equation for ‘f’:
          f x
            = y
            where
                y :: a
                y = x
    • Relevant bindings include
        y :: a1 (bound at ErrMsg.hs:7:5)
        x :: a (bound at ErrMsg.hs:4:3)
        f :: a -> a (bound at ErrMsg.hs:4:1)
  |
7 |     y = x
  |

The fact that ScopedTypeVariables is never even mentioned is particularly egregious, in my opinion. Also, note that the same error message is produced with ScopedTypeVariables enabled, and doesn't mention using an explicit forall.

Suggested error message

When ScopedTypeVariables is off:

• Couldn't match expected type ‘a1’ with actual type ‘a’
  The type variable ‘a’ is not in scope in the type signature
  for ‘y’, therefore it gets renamed to ‘a1’ and is not the same
  as ‘a’
  Consider enabling ScopedTypeVariables and including an explicit
  forall in the type signature for ‘f’ in order to bring ‘a’ into scope
• Relevant type variables include
    ‘a1’ (bound at ErrMsg.hs:6:5-10, in the signature for ‘y’)
    ‘a’ (bound at ErrMsg.hs:3:1-11, in the signature for ‘f’)
• Relevant bindings include
    y :: a1 (bound at ErrMsg.hs:7:5)
    x :: a (bound at ErrMsg.hs:4:3)
    f :: a -> a (bound at ErrMsg.hs:4:1)
  |
7 |     y = x
  |         ^

When ScopedTypeVariables is on simply replace this line:

Consider enabling ScopedTypeVariables and including an explicit
forall in the type signature for ‘f’ in order to bring ‘a’ into scope

With this:

Consider including an explicit forall in the type signature for ‘f’ 
in order to bring ‘a’ into scope

This sort of error would also benefit greatly from having a further explanation linked. I've seen discussions of giving errors unique ids and having a collection of explanations that can be referenced. This seems like a good application for that.

MorrowM avatar Jun 29 '21 17:06 MorrowM

This specific issue is what I remember being confused by the most, as a beginner. It wasn't immediately clear to me that, the a I bound inside the function scope, wasn't the same as the a bound within the function type itself.

TotallyNotChase avatar Aug 12 '21 18:08 TotallyNotChase

Yes, I think this should be doable. Permit me to offer small tweaks to the new messages:

Without -XScopedTypeVariables

• Couldn't match expected type ‘a1’ with actual type ‘a’
  The type variable ‘a’ is not in scope in the type signature
  for ‘y’; it therefore is considered a fresh variable, not equal
  to any other. To bring ‘a’ into scope:
    1. Enable the ScopedTypeVariables extension
    2. Rewrite 'f :: a -> a' to 'f :: forall a. a -> a'.        
• In the expression: x
  In an equation for ‘y’: y = x
  In an equation for ‘f’:
      f x
        = y
        where
            y :: a
            y = x
• Relevant type variables include
    ‘a1’ (bound at ErrMsg.hs:6:5-10, in the signature for ‘y’)
    ‘a’ (bound at ErrMsg.hs:3:1-11, in the signature for ‘f’)
• Relevant bindings include
    y :: a1 (bound at ErrMsg.hs:7:5)
    x :: a (bound at ErrMsg.hs:4:3)
    f :: a -> a (bound at ErrMsg.hs:4:1)
  |
7 |     y = x
  |   

With -XScopedTypeVariables:

• Couldn't match expected type ‘a1’ with actual type ‘a’
  The type variable ‘a’ is not in scope in the type signature
  for ‘y’; it therefore is considered a fresh variable, not equal
  to any other. To bring ‘a’ into scope:
    Rewrite 'f :: a -> a' to 'f :: forall a. a -> a'.
• In the expression: x
  In an equation for ‘y’: y = x
  In an equation for ‘f’:
      f x
        = y
        where
            y :: a
            y = x 
• Relevant type variables include
    ‘a1’ (bound at ErrMsg.hs:6:5-10, in the signature for ‘y’)
    ‘a’ (bound at ErrMsg.hs:3:1-11, in the signature for ‘f’)
• Relevant bindings include
    y :: a1 (bound at ErrMsg.hs:7:5)
    x :: a (bound at ErrMsg.hs:4:3)
    f :: a -> a (bound at ErrMsg.hs:4:1)
  |
7 |     y = x
  |   

I have kept the context (the "In the ..." lines) and rephrased the suggestion somewhat. If we're happy with this, would someone make a GHC ticket?

goldfirere avatar Aug 30 '21 22:08 goldfirere

https://gitlab.haskell.org/ghc/ghc/-/issues/20308

MorrowM avatar Aug 30 '21 23:08 MorrowM

Assuming (knock on wood) https://github.com/ghc-proposals/ghc-proposals/pull/448 gets accepted, we have some new stuff to consider here.

module ErrMsg where

f :: forall a. a -> a
f x = y
  where
    y :: a
    y = x

With -XNoImplicitForAll, the second a is out of scope, and we get no type error. Nice and simple!

Without, I think the -XTypeAbstractions solutions are better:

module ErrMsg where

f :: forall a. a -> a
f @a x = y
  where
    y :: a
    y = x

or

module ErrMsg where

f :: forall a. a -> a
f @(..) x = y
  where
    y :: a
    y = x

Ericson2314 avatar Feb 18 '22 06:02 Ericson2314