Reading — step 1 of 7
Learn
Show and Read are the type classes for converting between values and strings. LYAH covers them as foundational; every serious Haskell type implements at least Show.
Show — converting to string
class Show a where
show :: a -> String
Derivable for most types:
data Point = Point Int Int deriving (Show)
show (Point 3 4) -- "Point 3 4"
print (Point 3 4) -- prints: Point 3 4
The print function is just putStrLn . show.
Custom Show — pretty printing
data Money = Money Int -- cents
instance Show Money where
show (Money c) =
let (dollars, cents) = c `divMod` 100
in '$' : show dollars ++ "." ++ pad (show cents)
where
pad s = replicate (2 - length s) '0' ++ s
show (Money 12345) -- "$123.45"
Writing your own Show instance gives you full control over output. Useful for value objects (Money, Date, etc.).
Read — parsing from string
class Read a where
readsPrec :: Int -> String -> [(a, String)]
Usage via the helper functions:
read "42" :: Int -- 42
read "3.14" :: Double -- 3.14
read "[1, 2, 3]" :: [Int] -- [1, 2, 3]
-- Read THROWS on invalid input:
read "abc" :: Int -- *** Exception: Prelude.read: no parse
Safer parsing — readMaybe and readEither
import Text.Read (readMaybe, readEither)
readMaybe "42" :: Maybe Int -- Just 42
readMaybe "abc" :: Maybe Int -- Nothing
readEither "42" :: Either String Int -- Right 42
readEither "abc" :: Either String Int -- Left "Prelude.read: no parse"
Use readMaybe or readEither, not bare read. The exception from read on invalid input is hard to catch and ugly to debug.
Show / Read round-trip
The convention: read . show should be identity for derived instances:
data Color = Red | Green | Blue deriving (Show, Read)
show Red -- "Red"
read "Red" :: Color -- Red
read "Green" :: Color -- Green
read "Yellow" :: Color -- *** Exception: no parse
Derive Read alongside Show for serialization to/from text. Custom Show + custom Read needs careful thought to keep this round-trip property.
Show vs Display vs ToJSON
Show is for DEBUGGING. Real applications use:
Display(fromtext-displayor RIO) — user-facing outputToJSON/FromJSON(fromaeson) — JSON serialization- Custom formatters for logs, CSV, etc.
Don't make Show output user-facing — it's meant for ghci and debugging.
Show and lists / tuples
show [1, 2, 3] -- "[1,2,3]"
show (1, "foo", True) -- "(1,\"foo\",True)"
show (Just 5) -- "Just 5"
show Nothing -- "Nothing"
Standard library types all have sensible Show instances. Lists, tuples, Maybe, Either — all printable out of the box.
When to derive vs hand-write
Derive Show for:
- Debugging
- Test output
- Data types you control
Hand-write Show when:
- Output should look different from the constructor (Money $123.45 not Money 12345)
- Internal structure should be hidden
- Round-trip with Read isn't needed
Common mistakes
readinstead ofreadMaybe— invalid input throws an exception. Always prefer the safe variants.- Custom Show that breaks round-trip with Read — if you want round-trip, derive both. Mix carefully.
- Show for user output — Show is for developers. Use a separate formatting function for users.
- Read for non-trivial parsing — Read works for simple types; for complex parsing use a parser library (megaparsec, parsec).
- Forgetting type annotation on read —
read "42"is ambiguous; needread "42" :: Int.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…