GHC 9.0 rejects default signature that 8.10 accepts
I originally noticed this issue in the barbies-2.0.2.0
library on Hackage, which compiles with GHC 8.10.4 but fails to compile on GHC 9.0.1. Here is a minimized version of the code that fails to compile on 9.0.1:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Bug where
import Data.Coerce
import Data.Kind
import Data.Proxy
import GHC.Generics
import GHC.TypeNats
class FunctorT (t :: (k -> Type) -> k' -> Type) where
tmap :: forall f g
. (forall a . f a -> g a)
-> (forall x . t f x -> t g x)
-- Default implementation of 'tmap' based on 'Generic'.
default tmap
:: forall f g
. (forall a . f a -> g a)
-> (forall x. CanDeriveFunctorT t f g x => t f x -> t g x)
tmap f = toP (Proxy @1) . gmap (Proxy @1) f . fromP (Proxy @1)
type CanDeriveFunctorT t f g x
= ( GenericP 1 (t f x)
, GenericP 1 (t g x)
, GFunctor 1 f g (RepP 1 (t f x)) (RepP 1 (t g x))
)
-----
class GFunctor (n :: Nat) f g repbf repbg where
gmap :: Proxy n -> (forall a . f a -> g a) -> repbf x -> repbg x
class
( Coercible (Rep a) (RepP n a)
, Generic a
) => GenericP (n :: Nat) (a :: Type) where
type family RepP n a :: Type -> Type
toP :: Proxy n -> RepP n a x -> a
fromP :: Proxy n -> a -> RepP n a x
The important bit is the default signature for tmap
. With GHC 9.0.1, this is rejected thusly:
$ /opt/ghc/9.0.1/bin/ghc Bug.hs
[1 of 1] Compiling Bug ( Bug.hs, Bug.o )
Bug.hs:24:11: error:
• The default type signature for tmap:
forall (f :: k -> *) (g :: k -> *).
(forall (a :: k). f a -> g a)
-> forall (x :: k'). CanDeriveFunctorT t f g x => t f x -> t g x
does not match its corresponding non-default type signature
• When checking the class method:
tmap :: forall k k' (t :: (k -> *) -> k' -> *) (f :: k -> *)
(g :: k -> *).
FunctorT t =>
(forall (a :: k). f a -> g a) -> forall (x :: k'). t f x -> t g x
In the class declaration for ‘FunctorT’
|
24 | default tmap
| ^^^^
Why does GHC 9.0.1 reject this? According to the GHC User's Guide section on DefaultSignatures
:
The type signature for a default method of a type class must take on the same form as the corresponding main method’s type signature. Otherwise, the typechecker will reject that class’s definition. By “take on the same form”, we mean that the default type signature should differ from the main type signature only in their contexts.
As far as I can tell, tmap
meets this criterion, as the only difference between the two signatures is the presence of CanDeriveFunctorT t f g x =>
in the default signature.