{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverlappingInstances #-} ----------------------------------------------------------------------------- -- | -- Module : Generics.EMGM.Functions.Show -- Copyright : (c) 2008 Universiteit Utrecht -- License : BSD3 -- -- Maintainer : generics@haskell.org -- Stability : experimental -- Portability : non-portable -- -- Summary: Generic functions that convert values to readable strings. -- -- The functions in this module involve generically producing a string from a -- value of a supported datatype. The functions 'showsPrec' and 'show' are -- modeled after those in the class @Show@, and 'shows' after the related -- function of the same name. -- -- The underlying unparser is designed to be as similar to @deriving Show@ as -- possible. Refer to documentation in "Text.Show" for details. -- -- Since this library does not have access to the syntax of a @data@ -- declaration, it relies on 'ConDescr' for information. It is important that -- 'ConDescr' accurately describe, for each constructor, the name, arity, record -- labels (in same order as declared) if present, and fixity. -- -- See also "Generics.EMGM.Functions.Read". ----------------------------------------------------------------------------- module Generics.EMGM.Functions.Show ( Show(..), showsPrec, shows, show, ) where import Prelude hiding (Show, showsPrec, show, shows) import qualified Prelude as P (Show, showsPrec, show) import qualified GHC.Show as GHC (showList__) import Generics.EMGM.Common ----------------------------------------------------------------------------- -- Types ----------------------------------------------------------------------------- type ShowsPrec a = Int -> a -> ShowS -- | The type of a generic function that takes a constructor-type argument, a -- number (precedence), and a value and returns a 'ShowS' function. newtype Show a = Show { selShow :: ConType -> Int -> a -> ShowS } -- NOTE: Use full type here instead of 'ShowsPrec' for Haddock. ----------------------------------------------------------------------------- -- Utility functions ----------------------------------------------------------------------------- showSpace :: ShowS showSpace = showChar ' ' showBraces :: ShowsPrec a -> ShowsPrec a showBraces showsPrec' p x = showChar '{' . showsPrec' p x . showChar '}' showTuple :: [ShowS] -> ShowS showTuple ss = showParen True $ foldr1 (\s r -> s . showChar ',' . r) ss recEntry :: Bool -> String -> ShowsPrec a -> ShowsPrec a recEntry comma label showsPrec' _ x = showString label . showString " = " . showsPrec' minPrec x . -- Reset precedence for record fields showString (if comma then ", " else "") ----------------------------------------------------------------------------- -- Generic instance declaration ----------------------------------------------------------------------------- rconstantShow :: (P.Show a) => ConType -> ShowsPrec a rconstantShow ct = case ct of -- Standard constructor ConStd -> P.showsPrec -- Record-style constructor with 1 label ConRec (label:[]) -> recEntry False label P.showsPrec -- No other patterns expected other -> error $ "rconstantShow: Unexpected constructor: '" ++ P.show other ++ "'" rsumShow :: Show a -> Show b -> ConType -> ShowsPrec (a :+: b) rsumShow ra _ _ p (L a) = selShow ra ConStd p a rsumShow _ rb _ p (R b) = selShow rb ConStd p b rprodShow :: Show a -> Show b -> ConType -> ShowsPrec (a :*: b) rprodShow ra rb ct p (a :*: b) = case ct of -- Standard nonfix constructor ConStd -> selShowStep ra ConStd p a . showSpace . selShowStep rb ConStd p b -- Standard infix constructor ConIfx symbol -> selShowStep ra ConStd p a . showSpace . showString symbol . showSpace . selShowStep rb ConStd p b -- Record-style constructor ConRec (label:labels) -> let p' = p + 1 in recEntry True label (selShowStep ra ConStd) p' a . selShowStep rb (ConRec (labels)) p' b -- No other patterns expected other -> error $ "rprodShow: Unexpected constructor: '" ++ P.show other ++ "'" where selShowStep r ct' = selShow r ct' . (+1) rconShow :: ConDescr -> Show a -> ConType -> ShowsPrec a rconShow cd ra _ p a = case cd of -- Standard nonfix constructor ConDescr name arity [] Nonfix -> let hasArgs = arity > 0 in -- Don't show parens if constructor has no arguments showParen (p > appPrec && hasArgs) $ showString name . showString (if hasArgs then " " else "") . showConStep ConStd appPrec a -- Standard infix constructor ConDescr name _ [] fixity -> let conPrec = prec fixity in showParen (p > conPrec) $ showConStep (ConIfx name) conPrec a -- Record-style nonfix constructor ConDescr name _ labels Nonfix -> -- NOTE: Technically, we can use 'recPrec' below, because the precedence -- for record construction is higher than function application. However, -- since GHC puts parens for 'appRec', we'll put them. That way, we can -- compare string output to deriving Show for testing. showParen (p > appPrec) $ showString name . showSpace . showBraces (selShow ra (ConRec labels)) minPrec a -- Record-style infix constructor ConDescr name _ labels _ -> showParen True (showString name) . showSpace . showBraces (showConStep (ConRec labels)) p a where showConStep ct = selShow ra ct . (+1) rtypeShow :: EP b a -> Show a -> ConType -> ShowsPrec b rtypeShow ep ra ct = case ct of -- Standard constructor ConStd -> selShowFrom ConStd -- Record-style constructor ConRec (label:[]) -> recEntry False label (selShowFrom ConStd) -- No other patterns expected other -> error $ "rtypeShow: Unexpected constructor: '" ++ P.show other ++ "'" where selShowFrom ct' p = selShow ra ct' p . from ep instance Generic Show where rconstant = Show rconstantShow rsum ra rb = Show (rsumShow ra rb) rprod ra rb = Show (rprodShow ra rb) rcon cd ra = Show (rconShow cd ra) rtype ep ra = Show (rtypeShow ep ra) ----------------------------------------------------------------------------- -- Rep instance declarations ----------------------------------------------------------------------------- -- | Ad-hoc instance for lists instance (Rep Show a) => Rep Show [a] where rep = Show $ const $ const $ GHC.showList__ $ selShow rep ConStd minPrec -- | Ad-hoc instance for strings instance Rep Show String where rep = Show $ const P.showsPrec -- | Ad-hoc instance for @()@ instance Rep Show () where rep = Show $ const P.showsPrec -- | Ad-hoc instance for @(a,b)@ instance (Rep Show a, Rep Show b) => Rep Show (a,b) where rep = Show s where s _ _ (a,b) = showTuple [shows a, shows b] -- | Ad-hoc instance for @(a,b,c)@ instance (Rep Show a, Rep Show b, Rep Show c) => Rep Show (a,b,c) where rep = Show s where s _ _ (a,b,c) = showTuple [shows a, shows b, shows c] -- | Ad-hoc instance for @(a,b,c,d)@ instance (Rep Show a, Rep Show b, Rep Show c, Rep Show d) => Rep Show (a,b,c,d) where rep = Show s where s _ _ (a,b,c,d) = showTuple [shows a, shows b, shows c, shows d] -- | Ad-hoc instance for @(a,b,c,d,e)@ instance (Rep Show a, Rep Show b, Rep Show c, Rep Show d, Rep Show e) => Rep Show (a,b,c,d,e) where rep = Show s where s _ _ (a,b,c,d,e) = showTuple [shows a, shows b, shows c, shows d, shows e] -- | Ad-hoc instance for @(a,b,c,d,e,f)@ instance (Rep Show a, Rep Show b, Rep Show c, Rep Show d, Rep Show e, Rep Show f) => Rep Show (a,b,c,d,e,f) where rep = Show s where s _ _ (a,b,c,d,e,f) = showTuple [shows a, shows b, shows c, shows d, shows e, shows f] -- | Ad-hoc instance for @(a,b,c,d,e,f,h)@ instance (Rep Show a, Rep Show b, Rep Show c, Rep Show d, Rep Show e, Rep Show f, Rep Show h) => Rep Show (a,b,c,d,e,f,h) where rep = Show s where s _ _ (a,b,c,d,e,f,h) = showTuple [shows a, shows b, shows c, shows d, shows e, shows f, shows h] ----------------------------------------------------------------------------- -- Exported functions ----------------------------------------------------------------------------- -- | Convert a value to a readable string starting with the operator precedence -- of the enclosing context. showsPrec :: (Rep Show a) => Int -- ^ Operator precedence of the enclosing context (a number from 0 to 11). -> a -- ^ The value to be converted to a 'String'. -> ShowS showsPrec = selShow rep ConStd -- | A variant of 'showsPrec' with the minimum precedence (0). shows :: (Rep Show a) => a -> ShowS shows = showsPrec 0 -- | A variant of 'shows' that returns a 'String' instead of 'ShowS'. show :: (Rep Show a) => a -> String show = flip shows ""