[Haskell-cafe] Re: How to pretty print code efficiently

Achim Schneider barsoap at web.de
Fri Jul 3 22:56:48 EDT 2009

John Ky <newhoggy at gmail.com> wrote:

> Hi,
> Currently I'm pretty printing code by building arrays of strings and
> calling indent.  For example:
> instance JavaPrintableNamed AST.EnumeratedType where
>    javaLinesNamed parentName (AST.EnumeratedType memberDefinitions) =
>       [ "public enum " ++ asJavaId(parentName)
>       , "{"
>       ] ++ memberCodeLines ++
>       [ "}"
>       , ""
>       ]
>       where
>          memberCodeLines = indent $ javaLines memberDefinitions
> The indent function takes a list of strings and adds an indent to the
> beginning of every line.
> I can imagine this to be very inefficient as it builds many strings
> and concatenates them.
Yes and no. When concatenating two lists, only the first one is
rebuild, the second one is reused, due to sharing. So if you
concatenate short strings to the front of a long string you're quite
fine. But then, that isn't the answer you hoped for.

> In Ruby, I might do the same thing like this:
> class EnumeratedType < JavaPrintableNamed
>    def writeTo(writer)
>       writer.print "public enum "
>       writer.puts self.asJavaId
>       writer.puts "{"
>       writer.indent do
>          self.memberDefinitions.writeTo(writer)
>          writer.puts
>       end
> where above, the writer.indent takes care of the indent, and
> everything is appended to a stream, which doesn't seem so bad in
> terms of efficiency.
> I'm looking for a way to do something similar in Haskell.
> Anyone can give me a hand?
As data structure use Data.ByteString.Lazy: concatenating two lazy
bytestrings doesn't involve rebuilding the first string byte-by-byte,
but constructs a superstructure denoting the concatenation.

As for how to express it in code: I'd recommend a combination of a State
monad to track the indentation, and the underused[1] Applicative
interpretation of lists to concatenate stuff. >>= would function as
concatenation of lines, getting the state, while the indent function
would first set it to the new level, then execute the passed
sub-action, and finally reset it to the old level. You're going to need
a way to concatenate two strings without doing a line-break, too, of

All in all, it's a splendid exercise in how to write custom monads.

[1] Lists as ordered collections, not possibilities, that is, see e.g.
    the typeclassopedia 
    ( http://www.haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf )

(c) this sig last receiving data processing entity. Inspect headers
for copyright history. All rights reserved. Copying, hiring, renting,
performance and/or quoting of this signature prohibited.

More information about the Haskell-Cafe mailing list