Rossberg writes,

  thinking about whether the pretty printer proposed by Wadler requires
  some changes to be efficient in a strict language, I stumbled over the
  the last case defining `flatten':
  
          flatten (x :<|> y) = flatten x
  
  I wonder why it is necessary here to recurse on x. The only point were a
  doc (x:<|>y) is constructed is in the function `group':
  
          group z   = flatten z :<|> z
  
  So the x above is always flat already. Wouldn't the equation
  
          flatten (x :<|> y) = x
  
  suffice? Doing recursion here seems to be unnecessary overhead. In
  particular, it prevents structure sharing between alternatives when
  grouping, because flatten rebuilds the whole doc tree (which might be
  more of a problem without laziness).

  Am I missing something?

You're not the one who missed something.  I didn't spot this
optimization because I had in mind the case where the user might use
<|> directly.  If we disallow this, your tricky optimisation is quite
sensible.  As it happens, it doesn't seem to improve time or space by
much, at least for the lazy version.  I include modified code below.

It's wonderful to have clever people reading and commenting on
my code.  Thanks!  -- P

-----------------------------------------------------------------------
Philip Wadler                             [EMAIL PROTECTED]
Bell Labs, Lucent Technologies      http://www.cs.bell-labs.com/~wadler
600 Mountain Ave, room 2T-402                   office: +1 908 582 4004
Murray Hill, NJ 07974-0636                         fax: +1 908 582 5857
USA                                               home: +1 908 626 9252
-----------------------------------------------------------------------

-- Pretty printer based on grouping
-- as in March 1998 version of `A prettier printer'
-- Philip Wadler, March 1998

-- Modified version based on suggestion of Andreas Rossberg
-- Philip Wadler, May 1998

-- Two optimized lines, marked below, exploit the invariant that
-- the left hand argument of :<|> must be result of applying flatten.

infixr 5                 :<|>
infixr 6                 :<>
infixr 6                 <>
                         
data  DOC                =  NIL
                         |  DOC :<> DOC
                         |  NEST Int DOC
                         |  TEXT String
                         |  LINE
                         |  DOC :<|> DOC
                         
data  Doc                =  Nil
                         |  String `Text` Doc
                         |  Int `Line` Doc
                         
nil                      =  NIL
x <> y                   =  x :<> y
nest i x                 =  NEST i x
text s                   =  TEXT s
line                     =  LINE

group (x :<|> y)         =  x :<|> y    -- *** remove line for unoptimized
group x                  =  flatten x :<|> x
                         
flatten NIL              =  NIL
flatten (x :<> y)        =  flatten x :<> flatten y
flatten (NEST i x)       =  NEST i (flatten x)
flatten (TEXT s)         =  TEXT s
flatten LINE             =  TEXT " "
flatten (x :<|> y)       =  x  -- *** replace by (flatten x) for unoptimized
                         
layout Nil               =  ""
layout (s `Text` x)      =  s ++ layout x
layout (i `Line` x)      =  '\n' : copy i ' ' ++ layout x
                         
copy i x                 =  [ x | _ <- [1..i] ]

best w k x               =  be w k [(0,x)]
                         
be w k []                =  Nil
be w k ((i,NIL):z)       =  be w k z
be w k ((i,x :<> y):z)   =  be w k ((i,x):(i,y):z)
be w k ((i,NEST j x):z)  =  be w k ((i+j,x):z)
be w k ((i,TEXT s):z)    =  s `Text` be w (k+length s) z
be w k ((i,LINE):z)      =  i `Line` be w i z
be w k ((i,x :<|> y):z)  =  better w k (be w k ((i,x):z)) (be w k ((i,y):z))
                         
better w k x y           =  if fits (w-k) x then x else y
                         
fits w x | w < 0         =  False
fits w Nil               =  True
fits w (s `Text` x)      =  fits (w - length s) x
fits w (i `Line` x)      =  True
                         
pretty w x               =  layout (best w 0 x)

------------------------------------------------------------------------
-- Utilities

space           =  text " "
x </> y         =  x <> line <> y
x <+> y         =  x <> space <> y
par x           =  text "(" <> x <> text ")"
                
stack           =  foldr1 (</>)
strip           =  foldr1 (<+>)
                
------------------------------------------------------------------------
-- Testing

data    Term    =  Term String [Term]

prTerm (Term n [])      =  text n
prTerm (Term n ts)      =  par (group (nest 2 (stack (text n : map prTerm ts))))

szTerm (Term n ts)      =  length n + length ts + sum (map szTerm ts)

mkTerm 0 i              =  Term (mkName i) []
mkTerm (d+1) i          =  Term (mkName i) (map (mkTerm d) (randoms i))

mkName i                =  [ 'x' | j <- [1..i] ]

randoms i               =  [ i*j `mod` p | j <- [2 .. i `mod` p] ]
                        where  p = 7

teststring w d i        =  pretty w (prTerm (mkTerm d i))
testshow w d i          =  putStrLn (teststring w d i)
test w d i              =  length (teststring w d i)

{-

Pretty> test 60 8 3
11402
(377229 reductions, 640227 cells, 7 garbage collections)
Pretty> test 60 9 3
28253
(901055 reductions, 1519954 cells, 16 garbage collections)

-}


Reply via email to