-
GHC 8.10.x Migration Guide
-
Compiler changes
- Implicit kind variable changes
- GHC infers fewer dependent kinds
- GHC is pickier about impredicative uses of constraints
- GHC is pickier about inferred type signatures
- GHC is pickier about requiring language extensions to match on GADT constructors
- TypeFamilyDependencies may require UndecidableInstances
- New RecordWildCards warnings
- Improvements to pattern match checking with user-defined COMPLETE sets
- Library changes
-
Compiler changes
GHC 8.10.x Migration Guide
This guide summarises the changes you may need to make to your code to migrate from GHC 8.8 to GHC 8.10. This guide complements the GHC 8.10.x release notes which should be consulted as well.
Compiler changes
Implicit kind variable changes
GHC 8.10 implements proposal 103, which means that GHC is much less likely to implicitly quantify kind variables than it used to be. Here are some examples of code which will no longer work with GHC 8.10:
-
Kind variables are no longer implicitly quantified when an explicit
forall
is used at the beginning of a function's type signature. For instance, the following will no longer work:{-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} f :: forall (a :: k). Proxy a f = Proxy
error: Not in scope: type variable ‘k’ | 6 | f :: forall (a :: k). Proxy a | ^
This is because
k
is implicitly quantified in the kind ofa
. Here are two potential ways to migrate this code:-
If you are using GHC 8.0 or later, you can simply quantify
k
explicitly:f :: forall k (a :: k). Proxy a f = Proxy
Note that GHC 8.0, 8.2, and 8.4 require enabling the
TypeInType
extension in order to do this. On GHC 8.6 or later, however, explicitly quantifying kind variables simply requires thePolyKinds
extension. -
If you need to support versions of GHC older than 8.0, you may find the following piece of
CPP
useful:#if __GLASGOW_HASKELL__ >= 800 # define KVS(kvs) kvs #else # define KVS(kvs) #endif f :: forall KVS(k) (a :: k). Proxy a f = Proxy
-
-
Kind variables are no longer implicitly quantified in data constructor declarations:
data T a = T1 (S (a :: k)) | forall (b::k). T2 (S b) -- no longer accepted data T (a :: k) = T1 (S (a :: k)) | forall (b::k). T2 (S b) -- still accepted
As the above examples show, code that breaks because of this change can generally be fixed by adding explicit kind signatures to the type variable binders of the data type itself.
-
Implicitly quantified kind variables are no longer put in front of other variables:
f :: Proxy (a :: k) -> Proxy (b :: j)
ghci> :t +v f -- old order: f :: forall k j (a :: k) (b :: j). Proxy a -> Proxy b ghci> :t +v f -- new order: f :: forall k (a :: k) j (b :: j). Proxy a -> Proxy b
This is a breaking change for users of
TypeApplications
. If you wish to restore the old order, then explicitly quantify the type variables off
. -
In type synonyms and type family equations, free variables on the right-hand side are no longer implicitly quantified unless used in an outermost kind annotation:
type T = Just (Nothing :: Maybe a) -- no longer accepted type T = Just Nothing :: Maybe (Maybe a) -- still accepted
GHC infers fewer dependent kinds
GHC 8.10 features improvements to its kind inference engine. One downside to this is that it is slightly more conservative about inferring dependent kinds than it used to be. For example, the following example will kind-check on old versions of GHC, but not with GHC 8.10:
{-# LANGUAGE DataKinds, GADTs, PolyKinds, ScopedTypeVariables #-}
module Foo where
import Data.Kind
data T a where
MkT1 :: T Int
MkT2 :: T Bool
data ST a :: T a -> Type where
SMkT1 :: ST Int MkT1
SMkT2 :: ST Bool MkT2
Foo.hs:11:20: error:
• Expected kind ‘T a’, but ‘MkT1’ has kind ‘T Int’
• In the second argument of ‘ST’, namely ‘MkT1’
In the type ‘ST Int MkT1’
In the definition of data constructor ‘SMkT1’
|
11 | SMkT1 :: ST Int MkT1
| ^^^^
To fix this issue in a backwards-compatible way, one can give ST
a complete, user-specified kind signature (consult the relevant users' guide section for a more detailed description on what this means). In other words, apply the following change:
-data ST a :: T a -> Type where
+data ST (a :: Type) :: T a -> Type where
GHC is pickier about impredicative uses of constraints
Now that issue #11514 (closed) has been fixed, GHC is more stringent about impredicative uses of constraints. For example, the following program would be accepted on older versions of GHC, but not with GHC 8.10 or later:
foo :: forall a. (Show a => a -> a) -> ()
foo = undefined
foo
is impredicative since it attempts to instantiate undefined
at type (Show a => a -> a) -> ()
. This can be fixed by simply adding the necessary wildcard patterns, like so:
foo :: forall a. (Show a => a -> a) -> ()
foo _ = undefined
GHC is pickier about inferred type signatures
GHC now performs more validity checks on inferred type signatures. One consequence of this change is that some programs that used to be accepted will no longer compile without enabling the required language extensions. For example, in these two modules:
{-# LANGUAGE RankNTypes #-}
module A where
foo :: (forall a. a -> a) -> b -> b
foo f x = f x
module B where
import A
bar = foo
Notice that A
enables RankNTypes
, but B
does not. Previous versions of GHC would allow bar
to typecheck, even though its inferred type is higher-rank. GHC 8.10 will now reject this, as one must now enable RankNTypes
in B
to accept the inferred type signature.
GHC is pickier about requiring language extensions to match on GADT constructors
Given this module:
{-# LANGUAGE GADTs #-}
module A where
data T a where
MkT1 :: (Int ~ a) => T a
MkT2 :: T Bool
Previous versions of GHC would typecheck this code:
module B where
import A
f :: T Int -> ()
f MkT1 = ()
But not this code:
module C where
import A
f :: T Bool -> ()
f MkT2 = ()
C.hs:6:3: error:
• A pattern match on a GADT requires the GADTs or TypeFamilies language extension
• In the pattern: MkT2
In an equation for ‘f’: f MkT2 = ()
|
6 | f MkT2 = ()
| ^^^^
Note that both MkT1
and MkT2
are GADT constructors, however. GHC 8.10 fixes this inconsistency and now requires enabling GADTs
or TypeFamilies
on both the B
and C
examples above.
TypeFamilyDependencies
may require UndecidableInstances
Type family dependencies (also known as injective type families) sometimes now need UndecidableInstances
in order to be accepted. Here is an example:
type family F1 a = r | r -> a
type family F2 a = r | r -> a
type instance F2 [a] = Maybe (F1 a)
Because GHC needs to look under a type family to see that a
is determined by the right-hand side of F2
's equation, this now needs UndecidableInstances
. The problem is very much akin to its need to detect some functional dependencies.
RecordWildCards
warnings
New GHC 8.10 introduces two new flags, -Wunused-record-wildcards
and -Wredundant-record-wildcards
, which are implied by -Wall
. This means that certain programs that use RecordWildCards
will trigger warnings that did not do so previously. For instance, the following program emits no warnings on old versions of GHC, but will warn with GHC 8.10:
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -Wall #-}
data P = P { x :: Int, y :: Int }
f1 :: P -> Int
f1 P{..} = 1 + 3
f2 :: P -> Int
f2 P{x,y,..} = x + y
Foo.hs:8:6: warning: [-Wunused-record-wildcards]
No variables bound in the record wildcard match are used
Possible fix: omit the ‘..’
|
8 | f1 P{..} = 1 + 3
| ^^
Foo.hs:11:10: warning: [-Wredundant-record-wildcards]
Record wildcard does not bind any new variables
Possible fix: omit the ‘..’
|
11 | f2 P{x,y,..} = x + y
| ^^
To fix the warnings, simply remove the uses of ..
, as neither use is necessary.
Improvements to pattern match checking with user-defined COMPLETE sets
Previously, the user guide specified a couple of heuristics to define how pattern match warnings with user-defined COMPLETE sets are handled. Now these heuristics (and surprising behavior such as in #13363 (closed)) are gone and it should "just work". This means that potentially more and more accurate warnings are generated, which might lead to compilation errors with -Werror
.
An example from the wild is this catch-all match which is now correctly flagged as redundant, because Con'
, App
and Fun
form a COMPLETE set.
Deletion of redundant matches should always get rid of the warning without introducing new inexhaustiveness warnings (or, worse, introducing inexhaustiveness crashes at runtime without producing a warning).
Apart from that, redundancy checking around pattern guards improved, all while the performance of the checker increased.
Library changes
ghc-8.10
New hierarchical module structure
TODO: Mention https://gitlab.haskell.org/ghc/ghc/wikis/module-dependencies/hierarchical
template-haskell-2.16.0.0
Lift
changes
The Lift
class features a new method liftTyped :: t -> Q (TExp t)
. To facilitate making it easier to define implementations of lift
in terms of liftTyped
, the default implementation of lift
has been changed. Before, the default implementation used the Data
type class:
default lift :: Data t => t -> Q Exp
lift = liftData
The new default implementation is in terms of liftTyped
:
default lift :: (r ~ 'LiftedRep) => t -> Q Exp
lift = unTypeQ . liftTyped
This will affect any Lift
instance that does not explicitly define lift
. There are two ways to adapt to this change:
- Derive the
Lift
instance using theDeriveLift
extension, which has been available since GHC 8.0. - Explicitly define
liftTyped
, guarding it with#if MIN_VERSION_template_haskell(2,16,0) ... #endif
if you want to preserve backwards compatibility with older versions oftemplate-haskell
.
TupE
/UnboxedTupE
changes
The types of the TupE
and UnboxedTupE
constructors have changed:
data Exp
= ...
- | TupE [Exp]
- | UnboxedTupE [Exp]
+ | TupE [Maybe Exp]
+ | UnboxedTypE [Maybe Exp]
This is because TupE
and UnboxedTupE
are now capable of handling tuple sections. For example, (1,)
is represented with TupE [Just (LitE (IntegerL 1)), Nothing]
as an Exp
.
Unary tuple changes
Using TupleT 1
, TupE [exp]
, or TupP [pat]
will now produce unary tuples (i.e., involving the Unit
type from GHC.Tuple
) instead of silently dropping the parentheses. This brings Template Haskell's treatment of boxed tuples in line with that of unboxed tuples, as UnboxedTupleT
, UnboxedTupE
, and UnboxedTupP
also produce unary unboxed tuples (i.e., Unit#
) when applied to only one argument.