Convert to cabal.project
[packages/containers.git] / containers / src / Utils / Containers / Internal / TypeError.hs
1 {-# LANGUAGE DataKinds, FlexibleInstances, FlexibleContexts, UndecidableInstances,
2 KindSignatures, TypeFamilies, CPP #-}
3
4 #if !defined(TESTING)
5 # if __GLASGOW_HASKELL__ >= 710
6 {-# LANGUAGE Safe #-}
7 # else
8 {-# LANGUAGE Trustworthy #-}
9 #endif
10 #endif
11
12 -- | Unsatisfiable constraints for functions being removed.
13
14 module Utils.Containers.Internal.TypeError where
15 import GHC.TypeLits
16
17 -- | The constraint @Whoops s@ is unsatisfiable for every 'Symbol' @s@.
18 -- Under GHC 8.0 and above, trying to use a function with a @Whoops s@
19 -- constraint will lead to a pretty type error explaining how to fix
20 -- the problem. Under earlier GHC versions, it will produce an extremely
21 -- ugly type error within which the desired message is buried.
22 --
23 -- ==== Example
24 --
25 -- @
26 -- oldFunction :: Whoops "oldFunction is gone now. Use newFunction."
27 -- => Int -> IntMap a -> IntMap a
28 -- @
29 class Whoops (a :: Symbol)
30
31 #if __GLASGOW_HASKELL__ >= 800
32 instance TypeError ('Text a) => Whoops a
33 #endif
34
35 -- Why don't we just use
36 --
37 -- type Whoops a = TypeError ('Text a) ?
38 --
39 -- When GHC sees the type signature of oldFunction, it will see that it
40 -- has an unsatisfiable constraint and reject it out of hand.
41 --
42 -- It seems possible to hack around that with a type family:
43 --
44 -- type family Whoops a where
45 -- Whoops a = TypeError ('Text a)
46 --
47 -- but I don't really trust that to work reliably. What we actually
48 -- do is pretty much guaranteed to work. Despite the fact that there
49 -- is a totally polymorphic instance in scope, GHC will refrain from
50 -- reducing the constraint because it knows someone could (theoretically)
51 -- define an overlapping instance of Whoops. It doesn't commit to
52 -- the polymorphic one until it has to, at the call site.