src/Utils/Containers/Internal/TypeError.hs

    {-# LANGUAGE DataKinds, FlexibleInstances, FlexibleContexts, UndecidableInstances,
         KindSignatures, TypeFamilies, CPP #-}

    #if !defined(TESTING)
    # if __GLASGOW_HASKELL__ >= 710
    {-# LANGUAGE Safe #-}
    # else
    {-# LANGUAGE Trustworthy #-}
    #endif
    #endif

    -- | Unsatisfiable constraints for functions being removed.

    module Utils.Containers.Internal.TypeError where
    import GHC.TypeLits

    -- | The constraint @Whoops s@ is unsatisfiable for every 'Symbol' @s@.
    -- Under GHC 8.0 and above, trying to use a function with a @Whoops s@
    -- constraint will lead to a pretty type error explaining how to fix
    -- the problem. Under earlier GHC versions, it will produce an extremely
    -- ugly type error within which the desired message is buried.
    --
    -- ==== Example
    --
    -- @
    -- oldFunction :: Whoops "oldFunction is gone now. Use newFunction."
    --             => Int -> IntMap a -> IntMap a
    -- @
    class Whoops (a :: Symbol)

    #if __GLASGOW_HASKELL__ >= 800
    instance TypeError ('Text a) => Whoops a
    #endif

    -- Why don't we just use
    --
    -- type Whoops a = TypeError ('Text a) ?
    --
    -- When GHC sees the type signature of oldFunction, it will see that it
    -- has an unsatisfiable constraint and reject it out of hand.
    --
    -- It seems possible to hack around that with a type family:
    --
    -- type family Whoops a where
    --   Whoops a = TypeError ('Text a)
    --
    -- but I don't really trust that to work reliably. What we actually
    -- do is pretty much guaranteed to work. Despite the fact that there
    -- is a totally polymorphic instance in scope, GHC will refrain from
    -- reducing the constraint because it knows someone could (theoretically)
    -- define an overlapping instance of Whoops. It doesn't commit to
    -- the polymorphic one until it has to, at the call site.