-- | Combinators to write 'Show' instances. -- -- The following type illustrates the common use cases. -- -- @ -- data MyType a -- = C a a -- a regular constructor -- | a :+: a -- an infix constructor -- | R { f1 :: a, f2 :: a } -- a record -- -- infixl 4 :+: -- -- instance 'Show' a => 'Show' (MyType a) where -- 'showsPrec' = 'flip' precShows where -- precShows (C a b) = 'showCon' \"C\" '@|' a '@|' b -- precShows (c :+: d) = 'showInfix'' \":+:\" 4 c d -- precShows (R {f1 = e, f2 = f}) = -- 'showRecord' \"R\" (\"f1\" '.=.' e '&|' \"f2\" '.=.' f) -- @ module Text.Show.Combinators ( module Text.Show , PrecShowS -- * Simple constructors and applications , showCon , showApp , (@|) -- * Infix constructors , showInfix , showInfix' -- ** Combinators for associative operators -- | Use with care, see 'showInfixl'. , showInfixl , showInfixl' , showInfixr , showInfixr' -- * Records , ShowFields , showRecord , showField , (.=.) , noFields , appendFields , (&|) ) where import Text.Show -- | Type of strings representing expressions, parameterized by the surrounding -- precedence level. -- -- This is the return type of @'flip' 'showsPrec'@. type PrecShowS = Int -> ShowS -- | Show a constructor. -- -- Possible constructor names are: -- -- - regular constructors (e.g., @\"Left\"@); -- - parenthesized infix constructors (e.g., @\"(:)\"@); -- - smart constructors, for abstract types (e.g., @\"Map.fromList\"@). -- -- === __Example with smart constructor__ -- -- @ -- instance (Show k, Show v) => Show (Map k v) where -- showsPrec = 'flip' precShows where -- precShows m = 'showCon' \"Map.fromList\" '@|' Map.toList m -- -- -- Example output: -- -- > Map.fromList [(33, True), (55, False)] -- @ showCon :: String -> PrecShowS showCon con _ = showString con infixl 2 `showApp`, @| -- | Show a function application. showApp :: PrecShowS -> PrecShowS -> PrecShowS showApp showF showX d = showParen (d > appPrec) (showF appPrec . showSpace . showX appPrec1) -- | Show a function application. -- -- This is an infix shorthand for 'showApp' when the argument type is an -- instance of 'Show'. -- -- > showF @| x = showApp showF (flip showsPrec x) (@|) :: Show a => PrecShowS -> a -> PrecShowS (@|) showF x = showApp showF (flip showsPrec x) -- | Show an applied infix operator with a given precedence. showInfix :: String -> Int -> PrecShowS -> PrecShowS -> PrecShowS showInfix op prec = showInfix_ op prec (prec + 1) (prec + 1) -- | Show an applied infix operator with a given precedence. -- -- This is a shorthand for 'showInfix' when the arguments types are instances -- of 'Show'. -- -- > showInfix' op prec x y = -- > showInfix op prec (flip showsPrec x) (flip showsPrec y) showInfix' :: (Show a, Show b) => String -> Int -> a -> b -> PrecShowS showInfix' op prec x y = showInfix op prec (flip showsPrec x) (flip showsPrec y) -- | Show an applied infix operator which is left associative (@infixl@). -- Use with care. -- -- This combinator assumes that, if there is another infix operator to the -- left, it is either left associative with the same precedence, or it has a -- different precedence. -- An expression containing two operators at the same level with different -- associativities is ambiguous. -- -- By default, prefer 'showInfix' and 'showInfix''. showInfixl :: String -> Int -> PrecShowS -> PrecShowS -> PrecShowS showInfixl op prec = showInfix_ op prec prec (prec + 1) -- | Show an applied infix operator which is left associative (@infixl@). -- Use with care, see 'showInfixl'. -- -- This is a shorthand for 'showInfixl' when the arguments types are instances -- of 'Show'. -- -- By default, prefer 'showInfix' and 'showInfix''. showInfixl' :: (Show a, Show b) => String -> Int -> a -> b -> PrecShowS showInfixl' op prec x y = showInfixl op prec (flip showsPrec x) (flip showsPrec y) -- | Show an applied infix operator which is right associative (@infixr@). -- Use with care. -- -- This combinator assumes that, if there is another infix operator to the -- right, it is either right associative with the same precedence, or it has a -- different precedence. -- An expression containing two operators at the same level with different -- associativities is ambiguous. -- -- By default, prefer 'showInfix' and 'showInfix''. -- -- === __Example usage__ -- -- @ -- showList :: Show a => [a] -> PrecShowS -- showList [] = showCon "[]" -- showList (x : xs) = showInfixr ":" 5 (flip showsPrec x) (showList xs) -- -- -- Example output: -- -- > 0 : 1 : 2 : 3 : [] -- @ showInfixr :: String -> Int -> PrecShowS -> PrecShowS -> PrecShowS showInfixr op prec = showInfix_ op prec (prec + 1) prec -- | Show an applied infix operator which is right associative (@infixr@). -- Use with care, see 'showInfixr'. -- -- This is a shorthand for 'showInfixr' when the arguments types are instances -- of 'Show'. -- -- By default, prefer 'showInfix' and 'showInfix''. showInfixr' :: (Show a, Show b) => String -> Int -> a -> b -> PrecShowS showInfixr' op prec x y = showInfixr op prec (flip showsPrec x) (flip showsPrec y) -- | An internal combinator for infix operators, to explicitly update the -- precedence levels on each side. showInfix_ :: String -> Int -> Int -> Int -> PrecShowS -> PrecShowS -> PrecShowS showInfix_ op prec precX precY showX showY d = showParen (d > prec) (showX precX . showSpace . showString op . showSpace . showY precY) -- | Strings representing a set of record fields separated by commas. -- They can be constructed using ('.=.') and ('@|'), or using 'showField' and -- 'appendFields'. type ShowFields = ShowS -- | Show a record. The first argument is the constructor name. -- The second represents the set of record fields. showRecord :: String -> ShowFields -> PrecShowS showRecord con showFields _ = showString con . showSpace . showChar '{' . showFields . showChar '}' -- | Show a single record field: a field name and a value separated by @\'=\'@. showField :: String -> PrecShowS -> ShowFields showField field showX = showString field . showString " = " . showX 0 infixr 8 .=. -- | Show a single record field: a field name and a value separated by @\'=\'@. -- -- This is an infix shorthand for 'showField' when the value type is an -- instance of 'Show'. -- -- > field .=. x = showField field (flip showsPrec x) (.=.) :: Show a => String -> a -> ShowFields field .=. x = showField field (flip showsPrec x) -- | Empty set of record fields. noFields :: ShowFields noFields = id infixr 1 `appendFields`, &| -- | Separate two nonempty sets of record fields by a comma. appendFields :: ShowFields -> ShowFields -> ShowFields appendFields showFds1 showFds2 = showFds1 . showString ", " . showFds2 -- | An infix synonym of 'appendFields'. (&|) :: ShowFields -> ShowFields -> ShowFields (&|) = appendFields -- Helpers showSpace :: ShowS showSpace = (' ' :) appPrec, appPrec1 :: Int appPrec = 10 appPrec1 = 11