-- | When you have a value of @f a@, but you can not possibly have
-- some constraint on @a@, you often can not use type class instances
-- of @f a@, because they require instances for @a@. This module
-- provides type classes that mirror existing type classes but do not
-- have the constraint on @a@, usually at the cost of functionality.
{-# LANGUAGE CPP #-}
module Data.Functor.Classes.FreerConverse.Parametric
  ( ShowP(..)
  , defaultShowsPrecP
  ) where
import           Control.Applicative (Const)
import           Data.Proxy          (Proxy)

-- | Without a @Show a@ constraint it is impossible to show a value of type
-- @Maybe a@. It is, however, possible to distinguish between the @Just _@ and @Nothing cases.
--
-- This type class provides this functionality. It is similar to @Show1@ in @transformers@' @Data.Functor.Classes@, but without the @Show a@ constraint. Therefore, it can be used on quantified, unconstrained types like @forall a. Maybe a@.
class ShowP f where
  -- | Like 'showsPrec', but without using values of type @a@
  showsPrecP :: Int -> f a -> ShowS
  showsPrecP _ x s = showP x ++ s
  -- | Like 'show', but without using values of type @a@
  showP :: f a -> String
  showP x = showsPrecP 0 x ""

-- | Implements 'showsPrecP' using an instance 'Functor'@ f@ and an instance 'Show'@ a => @'Show'@ (f a)@
--
-- The values of type @a@ will be shown as @_@
defaultShowsPrecP :: (Functor f, Show (f Placeholder)) => Int -> f a -> ShowS
defaultShowsPrecP n x = showsPrec n (fmap (const Placeholder) x)

-- Not exported. For use by defaultShowsPrecP to get the 'right' Show instance to feed into the Show a => Show (f a) instance.
data Placeholder = Placeholder
instance Show Placeholder where
  show _ = "_"

-- | Equal to the Show instance
instance ShowP Proxy where
  showsPrecP = showsPrec
  showP = show

#if MIN_VERSION_base(4,8,0)
-- | Equal to the Show instance
instance Show a => ShowP (Const a) where
  showsPrecP = showsPrec
  showP = show
#endif

instance ShowP Maybe where
  showsPrecP = defaultShowsPrecP

instance ShowP [] where
  showsPrecP = defaultShowsPrecP

instance ShowP IO where
  showP _ = "<IO>"

instance Show a => ShowP (Either a) where
  showsPrecP = defaultShowsPrecP

instance Show a => ShowP ((,) a) where
  showsPrecP = defaultShowsPrecP