[Haskell-cafe] Re: Meaning of "ribbonsPerLine" at Text.PrettyPrint.HughesPJ ?

Benedikt Huber benjovi at gmx.net
Thu Jun 19 08:31:14 EDT 2008


Evan Laforge schrieb:
>> byorgey: fons: I can't explain it, all I know is that you must set it
>> to 1 or else it does bizarre things
>> fons: hahah, ok
>> fons: byorgey: that's funny considering its default value is 1.5
>> byorgey: if you set it to 1 then lineLength means what you think it should
>> byorgey: fons: EXACTLY
> 
> Excellent, thanks for solving a nagging problem I couldn't be bothered
> to track down.  I was wondering why my pretty printing was a little
> messed up and slightly too wide.
> 
> And isn't 100 columns a bit non-standard for a default?  I thought 80
> columns had more traction?  I know that's what my terminals are at...

Hi,

The "ribbon length" is used when choosing the most beautiful layout:
I'll just summarize the relevant section from John Hughes paper 
(http://www.cs.chalmers.se/~rjmh/Papers/pretty.ps), which explains it 
very nicely:

"... Using [the criterion whether the text fits on the page] alone tends 
to produce layouts such as

 > for i = 1 to 100; for j=1 to 100; for k=1 to 100; a[i][j][k]:=0;

which fits on a page {==> line-width} but cannot be described as pretty. 
We therefore impose an additional constraint limiting the number of 
characters on each line [==> ribbon-width} [...]

 > for i = 1 to 100
 >     for j = 1 to 100
 > ...
"

So the pretty printer tries to avoid sequences (ribbons) of characters 
which are longer than ribbon_length, when using auto layout stuff like 
`sep'.

In the source code, we have (paraphrased)

 > ribbon_length = line_length / ribbonsPerLine

and

 > choose_nicest_layout indent p q =
 >   if p + indent fits into line_length and p fits into ribbon_length
 >    then p
 >    else q

Working example below.

I'm not sure 80 characters is still standard when _pretty_-printing - 
the longest line in Text.PrettyPrint.HughesPJ is 109 characters wide ;)

Setting the ribbon ratio to 1 essentially disables the ribbon feature.

Btw: while studying the source code, I also found the cat (and sep) can 
be implemented in a more space efficient way (currently, cat needs to 
evaluate every document in a list to yield some output). Does this make 
sense (see below) ?

cheers,

benedikt


-- * ribbon example

 > -- lineLength = 26, ribbonsPerLine = 1.5 ==> ribbonLength = 17
 > -- therefore, we have a line break if width-indent > 17 or width > 26
 > testStyle = Style { mode = PageMode,
 >                    lineLength = 26,
 >                    ribbonsPerLine = 1.5 }
 > ribbonTest = renderStyle testStyle $
 >
 >   -- use hsep as width == 17 <= ribbonLength
 >       sep [ txt 5, txt 11 ]
 >
 >   -- linebreak, as width-indent = width = 18 > ribbonLength
 >   $+$ sep [ txt 5, txt 12 ]
 >
 >   -- use hsep, as width - indent == 17, and width == 22 < lineLength
 >   $+$ sep (map (nest 5) $ [txt 5, txt 11] )
 >
 >   -- linebreak, as width would be 27 > lineLength
 >   $+$ sep (map (nest 10) $ [txt 5, txt 11] )
 >
 > txt :: Int -> Doc
 > txt 0 = text ""
 > txt k = text $
 >  let ks = show k in
 >   (replicate (k - (length ks)) '_') ++ ks


-- * lazy variants of vcat and hcat
--   you need the constructors from the HughesPJ module

 > vcat' = foldAbove . foldr vcomp2 empty
 > hcat' = foldBeside . foldr hcomp2 empty
 >
 > foldAbove :: Doc -> Doc
 > foldAbove (Above Empty _ d2) = d2
 > foldAbove (Above d1 f d2) = Above d1 f $ foldAbove d2
 > foldAbove doc = doc
 >
 > vcomp2 :: Doc -> Doc -> Doc
 > vcomp2 d1 Empty = d1
 > -- do not match `vcomp2 Empty d1' !
 > vcomp2 d1 d2 = Above d1 False d2
 >
 > foldBeside :: Doc -> Doc
 > foldBeside (Beside Empty _ d2) = d2
 > foldBeside (Beside d1 f d2) = Beside d1 f $ foldBeside d2
 > foldBeside doc = doc
 >
 > hcomp2 :: Doc  -> Doc -> Doc
 > hcomp2 p Empty = p
 > hcomp2 p q = Beside p False q



More information about the Haskell-Cafe mailing list