« The Long Tail on SteamReal World Parsec »

Conveniently pretty-printing in Haskell

21/01/09

Permalink 04:28:11 pm, 480 words
Categories: Code

Conveniently pretty-printing in Haskell

I have been taking heat from my compatriots in the CL group who think that it’s a waste of time to switch to Haskell for prototyping. (A worthwhile waste, to be sure, but one whose payoff is too small when real work is still to be done.) I let slip that I hadn’t figured out how to make pretty-printing work. This is one of the things that Just Works in all the languages that matter, Python and Scheme, so it was perceived to be a black mark against Haskell’s real world utility.

So I wrote a small wrapper around Text.PrettyPrint.HughesPJ to prove to myself that it was actually almost there in terms of completeness. My example is about 20 lines of wrapper code. A search of Hackage turns up about 5 pretty printing libraries that appear to do just this, so I’ll probably download one of those and use it instead.

But if you don’t know Haskell, here’s an opportunity to see the closest thing it has to Java’s interfaces. They are actually closer to Ruby’s modules and are more powerful than you might expect. See QuickCheck and the Regex library for an example of how.

module Pretty (Pretty(..), pprint) where
import Text.PrettyPrint.HughesPJ

class Pretty a where
    prettyPrint :: a -> IO ()
    prettyPrint = print . prettyShow
    prettyShow :: a -> Doc

pprint :: Pretty a => a -> IO ()
pprint = prettyPrint

instance Pretty Double where
    prettyShow = double
instance Pretty a => Pretty [a] where
    prettyShow = brackets . sep . punctuate (text ", ") . map prettyShow
instance (Pretty a,Pretty b) => Pretty (a,b) where
    prettyShow (a,b) = parens (prettyShow a <> text ", " <> prettyShow b)
instance Pretty Char where
    prettyShow = char

class Pretty defines two generic functions. prettyPrint takes an instance of Pretty and prints it, while prettyShow just returns a Doc, which is a data type from HughesPJ that is itself Showable, so it knows how to convert itself to a string. Unlike Java, you can provide default implementations for the functions in terms of the other functions in the class. The compiler figures out the minimum needed for the implementation to work. However, I didn’t provide an implementation of prettyShow, so it’s required here. And prettyPrint is trivial, so I can’t imagine anybody re-implementing it.

Anyway, all that’s left to do is implement some instances of Pretty. My data structure right now is [(Double, Double)], so I just did list, pair and Double. Most of the functions, like brackets, sep and punctuate, come from HughesPJ, so they were really easy to write. I just can’t figure out why the library doesn’t come with these instances already written.

Oh, I also did Char to show you how Strings mess up since they are actually just lists of Char. pprint “foo” gives [f, o , o] instead of “foo”. There is a way to work around this but I haven’t looked it up yet.

No feedback yet

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)
blogging tool