Show
When we evaluate a numeric expression in GHCi, GHCi displays the result:
>>> 1 + 2
3
Or even more obviously, when we enter a number, GHCi prints the number we entered, because the value of this trivial expression is simply the number itself:
>>> 517
517
Now let's try this with banner numbers:
>>> newtype BannerNumber = B Int
>>> B 1
<interactive>:25:1: error:
• No instance for (Show BannerNumber) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
Huh, that didn't work so well. Clearly, to display some value on screen, GHCi needs to know how to convert it into a string that it can print. As I said before, any type we define does not support any operations out of the box. This includes conversion to a string to print it.
Show
is the class of all types that can be "shown", of all types for which we
have implemented a conversion to String
:
>>> :info Show
type Show :: * -> Constraint
class Show a where
showsPrec :: Int -> a -> ShowS
show :: a -> String
showList :: [a] -> ShowS
{-# MINIMAL showsPrec | show #-}
-- Defined in ‘GHC.Show’
[More omitted output]
Most of the time, the only function you care about is show
. It can be used to
convert a value of type a
to a corresponding string representation, of type
String
obviously. showsPrec
can be used to correctly format expressions so
they can be read back using readsPrec
, which is part of the Read
class
discussed next. The "Prec" part stands for "precedence". We won't discuss
showsPrec
in detail. showList
is a bit of a hack. Recall that a list of
integers is printed between square brackets: [1,4,2]
. A list of characters, on
the other hand, is printed as a string:
>>> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
This is enabled by showList
. showList
is used to display lists. The default
implementation displays the list elements between square brackets and separated
by commas. The showList
implementation for the Char
type overrides the
default implementation and instead prints the characters in the list between
double quotes.
Note, however, that the return type of showList
isn't String
. It's ShowS
.
showsPrec
has the same return type. This type is defined as
type ShowS = String -> String
It's a function from String
to String
. The reason why showsPrec
and
showList
have this type is a bit technical. When we convert a complex
structure into a string representation, we need to build up this string
representation from the string representations of the parts of our structure.
Now, since strings are lists of characters, this involves a whole lot of list
concatenations, and list concatenation is slow: it takes linear time in the
length of its first argument. Thus, the low level functions to convert values to
strings use an accumulator a bit like a StringBuilder
in Java. When building
up a string representation of a complex structure, we start with the empty
string. We call showsPrec
on the components of the structure, and each
component add its representation to this string, at the front, not at the
back, because prepending to a list is fast.