How to pretty print code efficiently

View: New views
6 Messages — Rating Filter:   Alert me  

How to pretty print code efficiently

by John Ky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

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?

Thanks

-John


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: How to pretty print code efficiently

by Achim Schneider :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

John Ky <newhoggy@...> 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
course.

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.


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: How to pretty print code efficiently

by Alexander Dunlap :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Fri, Jul 3, 2009 at 6:45 PM, John Ky<newhoggy@...> 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.
>
> 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?
>
> Thanks
>
> -John
>
>
> _______________________________________________

You may want to investigate the standard module
Text.PrettyPrint.HughesPJ, which contains a number of (I assume fairly
efficient) combinators for pretty printing.

Alex
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: How to pretty print code efficiently

by Achim Schneider :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Achim Schneider <barsoap@...> wrote:

> 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 course.
>
Make that "Reader Monad" and "execute the passed sub-action with the
new level inside a new monad"

--
(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.


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: How to pretty print code efficiently

by Chris Eidhof :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On 4 jul 2009, at 05:13, Alexander Dunlap wrote:

> On Fri, Jul 3, 2009 at 6:45 PM, John Ky<newhoggy@...> 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.
>>
>> 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?
>>
>> Thanks
>>
>> -John
>>
>>
>> _______________________________________________
>
> You may want to investigate the standard module
> Text.PrettyPrint.HughesPJ, which contains a number of (I assume fairly
> efficient) combinators for pretty printing.

I second that. Also, there is uulib which has a pretty printing module  
that's quite similar:

http://hackage.haskell.org/packages/archive/uulib/0.9.10/doc/html/UU-PPrint.html

I think both packages are based on the paper "The Design of a Pretty-
printing Library" which can be found at http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps

Not only do they provide abstractions for things like indentation,  
concatenation in different forms, etc., but they also are  more  
efficient than a naive implementation using lists.

-chris

-chris
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe

Re: How to pretty print code efficiently

by John Ky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi all,

Thanks everyone for the help.  The HughesPJ module works well for me.

Cheers,

-John

On Mon, Jul 6, 2009 at 3:49 AM, Chris Eidhof <chris@...> wrote:
On 4 jul 2009, at 05:13, Alexander Dunlap wrote:

On Fri, Jul 3, 2009 at 6:45 PM, John Ky<newhoggy@...> 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.

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?

Thanks

-John


_______________________________________________

You may want to investigate the standard module
Text.PrettyPrint.HughesPJ, which contains a number of (I assume fairly
efficient) combinators for pretty printing.

I second that. Also, there is uulib which has a pretty printing module that's quite similar:

http://hackage.haskell.org/packages/archive/uulib/0.9.10/doc/html/UU-PPrint.html

I think both packages are based on the paper "The Design of a Pretty-printing Library" which can be found at http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps

Not only do they provide abstractions for things like indentation, concatenation in different forms, etc., but they also are  more efficient than a naive implementation using lists.

-chris

-chris


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@...
http://www.haskell.org/mailman/listinfo/haskell-cafe