[Haskell-cafe] Don't make 'show*' functions

wren ng thornton wren at freegeek.org
Mon Dec 29 23:41:50 EST 2008


Thomas DuBuisson wrote:
> Jeff Heard proclaimed:
> > There are multiple distinct reasons people use Show, and this gets
> > confusing.
> 
> This is exactly what I was getting at.  I see four uses being discussed:

Indeed, though I think the situation is even worse. It seems to me that 
there are a number of cross-purposes for the Read+Show classes. The two 
main questions at stake are:


A) Who/what is the audience?

     Common answers:
     1) The human user on the other side of the terminal
     2) The human developer trying to debug their work
     3) The compiler, a la cut&paste
     4) A program on the other end of the wire/disk/flux capacitor

B) What is the resolution/detail?

     Common answers:
     1) All the gory details
     2) Enough for a human to get the big picture
     3) Enough for a computer to get the right value


The @a == (read . show) a@ interpretation is in A3/B1 territory, or 
A3/B3 if smart constructors are used. The GHCi and Hugs REPL are 
generally in A2/B1, or A1/B2 if we're trying to abstract away from the 
concrete implementation. The showTrie function mentioned at the outset 
of this thread is firmly in the A2/B2 category and is not intended for 
end users at all.

Clearly not all of these combinations can be serviced by a single class 
or pair of classes. Many of the combinations can themselves be broken 
down further (how much detail is "enough" to get the big picture?).

One of the things which has always disappointed me about the Read+Show 
classes is that they elide the profound difference between A1 and A2. 
Printing out a value for human consumption is entirely different than 
printing it out for debugging. The A3 compromise only serves to muddy 
things further and brings on the spectre of A4. For complex 
datastructures like Map, IntMap, and Trie there are many details stored 
in the structure which end users need not or should not know about; but 
these details are essential to the developers to ensure their code is 
doing what they think it should be. Similarly, for these large 
datastructures there is a profound difference in resolution between a 
derived Show class and an ASCII-art rendering of the tree. For small 
values ---and when you don't trust your tree drawer--- the derived 
instance is just what you want, but it quickly becomes unreadable for 
all but the most trivial examples.

Given the enormous design space involved here, there is no tractable 
answer that will cover everything. People have been working on user 
interfaces and data visualization for years, and no perfect answer has 
been found. But that doesn't mean we can't make progress. I agree with 
Thomas DuBuisson's suggestions and have rephrased them below, along with 
three new ones of my own.


Proposal 1: Combine Read and Show into a single class, "enforcing" @a == 
(read . show) a@ with the intention of capturing A3/B1 or A3/B3. While 
this may be serviceable for A1 or A2 uses, the intention of the class 
should be made clear that it is for A3. Presumably some solution should 
be found for types which can be read or shown but not both.


Proposal 2: Clean up Text.PrettyPrint.HughesPJ and market it heavily for 
covering code-oriented aspects of A1. Other visualizations like charts, 
graphs, or trees should be relegated elsewhere.


Proposal 3: Add a generic Lisp pretty printer function to convert from 
the output of Proposal 1 towards output like Proposal 2. Dealing with 
operators makes it trickier than Lisp, but most datastructures lack 
operators. I'm sure this has already been written in Haskell by Yi 
enthusiasts, and it should reduce the cost of getting people into using 
Pretty.


Proposal 4: Write a generic function for taking recursive types and 
printing them as a tree. Users should only need to serialize the "here" 
content of each node, leaving it up to the generic function to add 
spines and adequate spacing between nodes. This is for targeting A2/B2. 
While it's a time-honored tradition to implement specialized versions of 
this function to introduce people to recursion, making a standard 
version for visualizing large datastructures would alleviate some of the 
burden of what Show should be doing.


Proposal 5: Currently a main consumer of Show is the GHCi or Hugs REPL. 
However, those are used both by end users and by developers, which leads 
to the elision between A1 and A2. Provided the previous four proposals 
are taken to heart, it would be nice if GHCi and Hugs had commands to 
select which viewing mode (show, prettyShow, lispShow, treeShow) should 
be used for each type. By default, when available prettyShow should be 
favored over lispShow which is favored over show; but this behavior 
could be changed on a type-by-type basis. There are complications here 
regarding how to recurse for each element of a type, but they seem soluble.

-- 
Live well,
~wren


More information about the Haskell-Cafe mailing list