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

#if !defined(TESTING)
{-# LANGUAGE Safe #-}
#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@.  Trying
-- to use a function with a @Whoops s@ constraint will lead to a pretty type
-- error explaining how to fix the problem.
--
-- ==== Example
--
-- @
-- oldFunction :: Whoops "oldFunction is gone now. Use newFunction."
--             => Int -> IntMap a -> IntMap a
-- @
class Whoops (a :: Symbol)

instance TypeError ('Text a) => Whoops a

-- 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.