I'd like to write a horribly non-parametric version of a function of type

pretty :: (Show a) => a -> Text

such that

pretty :: Text -> Text = id pretty :: String -> Text = T.pack pretty :: (Show a) => a -> Text = T.pack . show

So the idea is that anything that already has a Show instance can be turned into a "pretty" Text by just show -ing it, except for Text and String which we want to special-case.

The following code works:

{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-} {-# LANGUAGE DataKinds, ConstraintKinds #-} module Pretty (pretty) where import Data.Text (Text) import qualified Data.Text as T type family StringLike a :: Bool where StringLike String = True StringLike Text = True StringLike a = False class (b ~ StringLike a) => Pretty' a b where pretty' :: a -> Text instance Pretty' String True where pretty' = T.pack instance Pretty' Text True where pretty' = id instance (Show a, StringLike a ~ False) => Pretty' a False where pretty' = T.pack . show type Pretty a = (Pretty' a (StringLike a)) pretty :: (Pretty a) => a -> Text pretty = pretty'

and it can be used without exporting anything except the pretty function.

However, I am not too happy about the type signature for pretty :

pretty :: (Pretty a) => a -> Text

I feel that since StringLike is a closed type family, there should be a way for GHC to figure out that if only (Show a) holds, (Pretty a) is already satisfied, since:

1. The following hold trivially just by substituting the results of applying StringLike: (StringLike String ~ True, Pretty' String True) (StringLike Text ~ True, Pretty' Text True) 2. For everything else, we *also* know the result of applying StringLike: (Show a, StringLike a ~ False) => (Pretty' a (StringLike a))

Is there a way to convince GHC of this?