| « The Long Tail on Steam | Real World Parsec » |
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.