basic-sop-0.3.0: src/Generics/SOP/Show.hs
-- | Generic show.
--
-- This module contains a generic show function defined using
-- @generics-sop@.
--
module Generics.SOP.Show (gshowsPrec, gshow) where
import Data.List (intersperse)
import Generics.SOP
-- | Generic show.
--
-- This function is a proof-of-concept implementation of a function
-- that is similar to the 'show' function you get by using
-- 'deriving Show'.
--
-- It serves as an example of an SOP-style generic function that makes
-- use of metadata.
--
-- If you want to use it on a datatype @T@ for which you have a
-- 'Generics.SOP.Generic' instance, you can use 'gshowsPrec' as
-- follows:
--
-- > instance Show T where
-- > showsPrec = gshowsPrec
--
gshowsPrec :: forall a. (Generic a, HasDatatypeInfo a, All2 Show (Code a))
=> Int -> a -> ShowS
gshowsPrec prec a =
gshowsPrec' prec (constructorInfo (datatypeInfo (Proxy :: Proxy a))) (from a)
gshow :: (Generic a, HasDatatypeInfo a, All2 Show (Code a)) => a -> String
gshow a = gshowsPrec 0 a ""
gshowsPrec' :: (All2 Show xss, SListI xss) => Int -> NP ConstructorInfo xss -> SOP I xss -> ShowS
gshowsPrec' prec cs (SOP sop) =
hcollapse $ hcliftA2 allp (goConstructor prec) cs sop
goConstructor :: All Show xs => Int -> ConstructorInfo xs -> NP I xs -> K ShowS xs
goConstructor prec (Constructor n) args =
K $
showParen
(fixity <= prec)
(foldr (.) id $ intersperse (showString " ") (showString n : args'))
where
args' :: [ShowS]
args' = hcollapse $ hcliftA p (K . showsPrec 11 . unI) args
-- With fixity = 11 the parens will be shown only if the enclosing
-- context is a function application. This is correct because
-- function application is the only thing that binds tightly
-- enough to force parens around this expression.
fixity = 11
goConstructor prec (Record n ns) args =
K $
showParen
(fixity <= prec)
(showString n . showString " {" . foldr (.) id (intersperse (showString ", ") args') . showString "}")
where
args' :: [ShowS]
args' = hcollapse $ hcliftA2 p goField ns args
-- With fixity = 12 the parens will never be shown. This is
-- correct because record construction binds tighter than even
-- function application!
fixity = 12
goConstructor prec (Infix n _ fixity) (I arg1 :* I arg2 :* Nil) =
K $
showParen
(fixity <= prec)
(showsPrec fixity arg1 . showString " " . showString n . showString " " . showsPrec fixity arg2)
#if __GLASGOW_HASKELL__ < 800
goConstructor _ (Infix _ _ _) _ = error "inaccessible"
#endif
goField :: Show a => FieldInfo a -> I a -> K ShowS a
goField (FieldInfo field) (I a) = K $ showString field . showString " = " . showsPrec 0 a
p :: Proxy Show
p = Proxy
allp :: Proxy (All Show)
allp = Proxy