4

My understanding of the correct implementation for Haskell's Show and Read is that read.show should be identity. If true, then either read needs to be able to do some pretty messy string parsing, or show has to provide fairly limited functionality (from a user experience point of view).

Is it the case that read.show == id, and if so what is the accepted Haskell practice — for function naming and invocation — for richer (string) representations of values?

Community
  • 1
  • 1
orome
  • 45,163
  • 57
  • 202
  • 418
  • `pprint` is usually pretty understandable as an alternatively to `show` that does pretty output. – bheklilr Sep 11 '15 at 18:18
  • 1
    My advice: do not use `read`: it fails with `error`. You can use things like `maybeRead` (or `reads` function), but I think that `attoparsec` library is better - it could produce cleaner messages. Not using `read` lifts the restriction. – Heimdell Sep 11 '15 at 18:26
  • @bheklilr: Can you say a bit more about how to implement and use `pprint`. For my purposes, the best "display" is something very much different from the underlying representation of the value, and needs a good bit of formatting to get there. (Reading that representation, btw, would make almost no sense, since no user would enter it.) – orome Sep 11 '15 at 18:32
  • `pprint :: PPrintOptions -> MyType -> String` or just `pprint :: MyType -> String`. I can't really provide any more tips without knowing what the structure is and how you want to display it, which would be a different question anyway. You can also optionally swap out `String` for `Text` or `ByteString` depending on your needs. – bheklilr Sep 11 '15 at 18:34
  • @bheklilr: What typeclass does `MyType` implement for `pprint`, and how is `pprint` invoked — `print` has the advantage of being "automatic", in GHCi, Jupyter Notebooks, and through `print`? (Awkward naming too; `print` actually prints, bit `pprint` really does something more like `pshow`, right?) – orome Sep 11 '15 at 18:42
  • 1
    @raxacoricofallapatorius There isn't any standard typeclass, if that's what you're asking. I just came up with a function name and a type signature. If you would prefer using `pshow` then that's fine by me, if you wanted a typeclass with it then `class PrettyShow a where pshow :: a -> String` and then `pprint :: PrettyShow a => a -> IO (); pprint = putStrLn . pshow`. – bheklilr Sep 11 '15 at 18:45
  • @Heimdell Beyond `maybeRead` (from the *utility-ht* package) there is also `readMaybe` in `Text.Read` (which is in *base* and is therefore more accessible). – duplode Sep 11 '15 at 20:49
  • 2
    The entire `Read` mechanism is a bit of a throwback, based on an interesting idea about parsing that later developed in other directions. Aside from the fact that `Read` instances are relatively hard to write, there's the bigger problem that they're not usually terribly useful. `Show` is primarily useful for debugging and interactive development in GHCi. I've not yet seen a killer app for `Read`. It's typically much more useful to write, say, `attoparsec` and/or `parsec` parsers, instances of serialization classes, `FromJSON` instances, etc. – dfeuer Sep 11 '15 at 21:29
  • @dfeuer: That (from my beginner vantage) seems accurate. `Show` seems like a debugging tool and not much more. It's certainly not for end use. The absence of a `PShow` typeclass [seems an omission](http://stackoverflow.com/q/32532617/656912). – orome Sep 11 '15 at 21:49

1 Answers1

5

First of all, show is expected to produce a string following the Haskell syntax -- ideally, one should be able to cut & paste that into a Haskell file and have that work.

That being said, read . show is expected to be the identity. However, sometimes it is relaxed a bit, only requiring that

read (show x) == x

where (==) is the equivalence defined by the related Eq instance. I believe the archetypal example for this is Data.Set.Set, which internally represents sets using balanced binary search trees (BST). Its Show instance produces strings of the form

fromList [1,2,3,4,6,7]

reading back such a string likely produces a different BST, but one which is still == to the old one. From the user's point of view, there's no (observable) difference.

When printing a custom "opaque" type the same fromSomeTransparentType value string encoding can often be used.

Finally, I guess these Show-related "social contracts" are much more respected in libraries than in applications. This is because in applications one sometimes can reasonably assume that no one else - but the application itself - will ever use these instances. E.g., I think this is okay-ish for an application:

data Exp = Add Exp Exp | ...
instance Show Exp where
   show (Add e1 e2) = "(" ++ show e1 ++ ", " ++ show e2 ++ ")"

The "other" law show . read == id does not hold, in general. First, read is partial. Second, even disregarding partiality, the read method is allowed to be more lenient on its input, e.g. treating repeated whitespace as a single one, or neglecting redundant parentheses: (as AJFarmar suggests below)

show (read "Just (((6)))") = "Just 6"
chi
  • 111,837
  • 3
  • 133
  • 218
  • I think you should clarify a bit by saying something like `show (read "Just (((6)))") = "Just 6"`, which is clearly not the identity, but nonetheless correct. – AJF Sep 11 '15 at 20:12
  • @AJFarmar I added a note, even if the OP asked about `read.show`, not `show.read`. Still, the latter can be interesting for comparison with the former. – chi Sep 11 '15 at 20:35