GHC 8.2.x Migration Guide

This guide summarises the changes you may need to make to your code to migrate from GHC 8.0 to GHC 8.2. This guide complements the GHC 8.2.x release notes which should be consulted as well.

Compiler changes

DefaultSignatures is pickier

There a new validity check for default class method implementations using -XDefaultSignatures. In particular, if you have a class Foo:

class Foo a where
  bar :: C => a -> b -> b

and you add a default type signature for bar, it must be of the form:

  default bar :: C' => a -> b -> b

That is, the right-hand sides of the type signatures must be the same, but the contexts C and C' are allowed to be different. That means that these default type signatures for bar:

  default bar :: C' => b -> a -> b
  default bar :: C' => b -> b -> a
  default bar :: C' => a -> b -> b -> b

will all be rejected.

These will also be rejected:

  default bar :: C' => a -> Int -> Int
  default bar :: C' => a -> TF b -> TF b

(where TF is a type family). But it's possible to rearrange these into equivalent forms that GHC accepts: just use type equalities!

  default bar :: (C', b ~ Int)  => a -> b -> b
  default bar :: (C', b ~ TF c) => a -> b -> b

As shown in the TF example, you might have to create new type variables (e.g., c) to make the type equalities work out.

Associated type family instances are pickier

There is also a new validity check for associated type family instances. That is, if you have a class with an associated type family:

class C a b where
  type T a x b

And you create and instance of C:

instance C ty1 ty2 where ...

Then the associated T instance must look exactly like:

  type T ty1 v ty2 = ...
  -- 'ty1' for 'a'
  -- 'ty2' for 'b', and
  -- some type `v` for 'x'

As a concrete example, this code, which would have been allowed before GHC 8.2, is now disallowed:

class Foo a where
  type Bar a

instance Foo (Either a b) where
  type Bar (Either c d) = d -> c

To fix this instance, simply use the same type variables in the Bar instance as in the instance head:

instance Foo (Either a b) where
  type Bar (Either a b) = b -> a

Instances for class synonyms are now disallowed

Previously, GHC silently accepted nonsense instance declarations like this:

type ReadShow a = (Read a, Show a)

instance Read Foo
instance Show Foo
instance ReadShow Foo

It's not even clear what this is supposed to mean, since ReadShow isn't a class in and of itself. To disallow this, GHC now prevents all instances of the form instance (...) => Syn (...), where Syn is a type synonym.

This check is a bit conservative, as it bars you from writing this as well:

type MyShow = Show
instance MyShow Foo

The workaround is to define the instance using Show instead of MyShow.

Kind generalization and MonoLocalBinds

The MonoLocalBinds extension, which places limitations on when the type of a term is generalized, also affects when the kind of a type signature of generalized. Prior to GHC 8.2, there were certain scenarios when kinds signatures were incorrectly generalized in the presence of MonoLocalBinds. This bug has now been fixed, but as a consequence, there are a handful of programs which will fail to typecheck without explicit kind signatures. Here is a known example:

{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeInType #-}

data Proxy a = Proxy
newtype Tagged s b = Tagged b

class C b where
  c :: forall (s :: k). Tagged s b

instance C (Proxy a) where
  c :: forall s. Tagged s (Proxy a)
  c = Tagged Proxy

This compiles in GHC 8.0, but in GHC 8.2, it gives this error:

    • Couldn't match type ‘k0’ with ‘k1’
        because type variable ‘k1’ would escape its scope
      This (rigid, skolem) type variable is bound by
        the type signature for:
          c :: forall k1 (s :: k1). Tagged s (Proxy a)
        at Bug.hs:13:8-35
      Expected type: Tagged s (Proxy a)
        Actual type: Tagged s (Proxy a)
    • When checking that instance signature for ‘c’
        is more general than its signature in the class
        Instance sig: forall (s :: k0). Tagged s (Proxy a)
           Class sig: forall k1 (s :: k1). Tagged s (Proxy a)
      In the instance declaration for ‘C (Proxy a)’

The reason for this error is that the type signature for c captures a, an outer-scoped type variable, which means the type signature is not closed. Therefore, with MonoLocalBinds enabled, the inferred kind for s will not be generalized, and as a result, it will fail to unify with the kind variable k which is specified in the declaration of c.

This can be worked around by specifying an explicit kind variable for s, e.g.,

instance C (Proxy a) where
  c :: forall (s :: k). Tagged s (Proxy a)
  c = Tagged Proxy

Library changes


Recommendations for forward-compatibility

In order to future-proof your packages for upcoming changes, add the following snippet to your .cabal file, and address the warnings emitted by GHC when compiling your package:

  if impl(ghc >= 8.0)
    ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances
    -- provide/emulate `Control.Monad.Fail` and `Data.Semigroups` API for pre-GHC8
    build-depends: fail == 4.9.*, semigroups == 0.18.*

See wiki:Migration/8.0#base- for more details

Type-indexed Typeable changes

  • The Data.Typeable.Internal module has been removed entirely. Much of the Typeable internals now live in the new Type.Reflection module.
  • Data.Typeable.TypeRep is now a type synonym (so you may need TypeSynonymInstances to create an instance for it, unless you switch it to use Type.Reflection.SomeTypeRep)
  • mkFunTy, mkAppTy, mkTyConApp, and have been removed. If you use these then you might instead consider looking at the new type-indexed interfaces found in Type.Reflection.
  • mkTyCon3 and mkTyConApp are no longer exported by Data.Typeable. They are instead exported by Type.Reflection.Unsafe.
  • Data.Dynamic no longer re-exports all of Data.Typeable (only the Typeable class).

fromLabel type signature change

The type signature of fromLabel has changed:

fromLabel :: Proxy# x -> a -- old type signature
fromLabel ::             a -- new type signature

The new fromLabel can be accommodated using TypeApplications:

fromLabel (proxy# :: Proxy# "foo") :: alpha -- old style
fromLabel @"foo"                   :: alpha -- new style

GHC.Generics is more polykinded

The Generic1 class, as well related classes and data types from GHC.Generics, are now poly-kinded. Here are the kind signatures of these types before base-

class Generic1 (f :: * -> *) where
  type Rep1 f :: * -> *
  from1  :: f a -> Rep1 f a
  to1    :: Rep1 f a -> f a

class Datatype (d :: k) where
  datatypeName :: t d (f :: * -> *) (a :: k1) -> [Char]
  moduleName   :: t d (f :: * -> *) (a :: k1) -> [Char]
  packageName  :: t d (f :: * -> *) (a :: k1) -> [Char]
  isNewtype    :: t d (f :: * -> *) (a :: k1) -> Bool

class Constructor (c :: k) where
  conName     :: t c (f :: * -> *) (a :: k1) -> [Char]
  conFixity   :: t c (f :: * -> *) (a :: k1) -> Fixity
  conIsRecord :: t c (f :: * -> *) (a :: k1) -> Bool

class Selector (s :: k) where
  selName               :: t s (f :: * -> *) (a :: k1) -> [Char]
  selSourceUnpackedness :: t s (f :: * -> *) (a :: k1) -> SourceUnpackedness
  selSourceStrictness   :: t s (f :: * -> *) (a :: k1) -> SourceStrictness
  selDecidedStrictness  :: t s (f :: * -> *) (a :: k1) -> DecidedStrictness

data V1 (p :: *)
data U1 (p :: *) = U1
newtype Par1 p = Par1 p
newtype Rec1 (f :: * -> *) (p :: *) = Rec1 (f p)
newtype K1 i c (p :: *) = K1 c
newtype M1 i c (f :: * -> *) (p :: *) = M1 (f p)
data (:+:) (f :: * -> *) (g :: * -> *) (p :: *) = L1 (f p) | R1 (g p)
data (:*:) (f :: * -> *) (g :: * -> *) (p :: *) = f p :*: g p
newtype (:.:) (f :: * -> *) (g :: * -> *) (p :: *) = Comp1 (f (g p))
data family URec a (p :: *)

And here are their kind signatures now:

class Generic1 (f :: k -> *) where
  type Rep1 f :: k -> *
  from1  :: f a -> Rep1 f a
  to1    :: Rep1 f a -> f a

class Datatype (d :: k) where
  datatypeName :: t d (f :: k1 -> *) (a :: k1) -> [Char]
  moduleName   :: t d (f :: k1 -> *) (a :: k1) -> [Char]
  packageName  :: t d (f :: k1 -> *) (a :: k1) -> [Char]
  isNewtype    :: t d (f :: k1 -> *) (a :: k1) -> Bool

class Constructor (c :: k) where
  conName     :: t c (f :: k1 -> *) (a :: k1) -> [Char]
  conFixity   :: t c (f :: k1 -> *) (a :: k1) -> Fixity
  conIsRecord :: t c (f :: k1 -> *) (a :: k1) -> Bool

class Selector (s :: k) where
  selName               :: t s (f :: k1 -> *) (a :: k1) -> [Char]
  selSourceUnpackedness :: t s (f :: k1 -> *) (a :: k1) -> SourceUnpackedness
  selSourceStrictness   :: t s (f :: k1 -> *) (a :: k1) -> SourceStrictness
  selDecidedStrictness  :: t s (f :: k1 -> *) (a :: k1) -> DecidedStrictness

data V1 (p :: k)
data U1 (p :: k) = U1
newtype Par1 p = Par1 p
newtype Rec1 (f :: k -> *) (p :: k) = Rec1 (f p)
newtype K1 i c (p :: k) = K1 c
newtype M1 i c (f :: k -> *) (p :: k) = M1 (f p)
data (:+:) (f :: k -> *) (g :: k -> *) (p :: k) = L1 (f p) | R1 (g p)
data (:*:) (f :: k -> *) (g :: k -> *) (p :: k) = f p :*: g p
newtype (:.:) (f :: k2 -> *) (g :: k1 -> k2) (p :: k1) = Comp1 (f (g p))
data family URec a (p :: k)

It's possible that you might experience some typechecker errors due to this change. If so, a probable fix is to add explicit kind signatures in the right places.


  • The DataD, NewtypeD, DataInstD, and NewtypeInstD constructors now take a [DerivCxtQ] instead of a CxtQ to represent deriving clauses (#10598). This change was necessary because:
    • Due to the introduction of deriving strategies, data types can now accept multiple deriving clauses (hence the need for [DerivCxtQ] instead of DerivCxtQ).
    • Each deriving clause now allows an optional strategy keyword, so a new DerivClause data type was introduced that contains a Maybe DerivStrategy in addition to the usual Cxt.

Similarly, the StandaloneDerivD constructor now also takes an additional Maybe DerivStrategy argument, since deriving strategy keywords can also be used with standalone deriving declarations. The standaloneDerivD function's type signature remains unchanged, as it will produce a standalone deriving declaration with no strategy keyword. If you want to use an explicit keyword, use standaloneDerivWithStrategyD.

Tool changes

GHC API changes

  • The StaticFlags module has been removed, as all static flags have been converted to dynamic ones in GHC 8.2.
Last modified 2 years ago Last modified on Jun 15, 2017 10:24:22 AM