[Haskell-cafe] Re: Knot tying vs monads
ChrisK
haskell at list.mightyreason.com
Mon Nov 19 11:07:19 EST 2007
John D. Ramsdell wrote:
On Nov 17, 2007 3:04 PM, apfelmus <apfelmus at quantentunnel.de
<mailto:apfelmus at quantentunnel.de>> wrote:
Unfortunately, I don't have Paulson's book (or any other ML book :) at
home. I'm too lazy to figure out the specification from the source code,
I guess the code is too opaque, as my colleague claimed.
Yes, a bit opaque. The variable names are not descriptive enough and not
documented. I have just rewritten your code with mostly trivial changes all of
which clarify what the code is doing in each expression.
For instance, blocksize and space* were both measured relative to "margin"
instead of simply using the column number.
The file is both att
> {-# LANGUAGE BangPatterns #-}
>
> -- Author: Chris Kuklewicz
> --
> -- This is a rewrite of John D. Ramsdell Pretty.hs code on
> -- haskell-cafe mailing list
>
> -- Changelog from Pretty.hs
> -- All Pretty elements have a length field (lazy at the moment)
> -- Inlined logic of 'blanks' and used new 'prepend' instead
> -- Replaced blocksize by startColumn == margin - blocksize
> -- Replaced space by colunmIn == margin-space
> -- Documented what 'after' means
> module Blocks(Pretty,str,brk,spc,blo,cat,pr) where
>
> -- All of the len's are non-negative, guaranteed by smart constructors
> data Pretty = Str { len :: Int, string :: String}
> | Brk { len :: Int }
> | Blo { len :: Int, indentBy :: Int, parts :: [Pretty] }
>
> str s = Str (length s) s
>
> brk n | n < 0 = error ("Cannot have negative width brk! n = " ++ show n)
> | otherwise = Brk n
>
> spc = brk 1
>
> blo indent es | indent < 0 = error ("Cannot have negative width blo! indent = " ++ show indent)
> | otherwise = Blo (sum (map len es)) indent es
>
> cat = blo 0
>
> {-# INLINE pr #-}
> pr :: Int -> Pretty -> (String->String)
> pr margin e sRest = let {startColumn = 0; after = 0; columnIn = 0}
> in snd (printing margin startColumn after [e] (columnIn, sRest))
>
> {-# INLINE printing #-}
> printing :: Int -> Int -> Int -> [Pretty] -> (Int,String) -> (Int,String)
> -- margin is the desired maximum line length and must be non-negative
> -- startColumn, columnIn, column', and columnOut are all non-negative,
> -- but any of them may be greater than margin
> printing margin | margin < 0 = error ("Cannot have non-positive margin! margin == "++show margin)
> | otherwise = block where
>
> -- startColumn is the "current amount of indent after newline"
> -- after is how much must be laid after the (e:es) and before the next break, non-negative
> block !startColumn !after = layout where
>
> -- (e:es) are the items to layout before 'sIn'
> -- columnIn is the starting column for laying out 'e'
> -- columnOut is the column after the (e:es) have been laid out
> layout [] columnIn'sIn'pair = columnIn'sIn'pair
> layout (e:es) (!columnIn,sIn) = (columnOut,sOut) where
>
> (columnOut,s') = layout es (column',sIn)
>
> -- column' is the column to use after when laying out es, after laying out e
> (column',sOut) =
> case e of
> Str n str -> (columnIn+n, showString str s')
> Brk n | columnIn + n + breakDist es after <= margin -> (columnIn+n, prepend n ' ' s')
> | 0 <= startColumn -> (startColumn, '\n':prepend startColumn ' ' s')
> | otherwise -> (0, '\n':s')
> Blo _n indent es' -> let startColumn' = indent + columnIn
> after' = breakDist es after
> in block startColumn' after' es' (columnIn,s')
>
> -- Trivial helper function to prepend 'n' copies of character 'c'
> {-# INLINE prepend #-}
> prepend n c s | n < 0 = error ("prepend called with "++show n++" < 0 !")
> | otherwise = loop n where loop 0 = s
> loop n = c : loop (pred n)
>
> -- after >=0 implies breakDist _ after >= 0
> -- Note that contained Blo's are assumed to layout in one line without using any internal breaks.
> breakDist :: [Pretty] -> Int -> Int
> breakDist esIn !after = accum esIn 0 where
> accum [] beforeBrk = beforeBrk + after
> accum (Brk {}:_) beforeBrk = beforeBrk
> accum (e : es) beforeBrk = accum es (beforeBrk + len e)
>
> test1 = putStrLn $
> pr 5 (blo 3 [str "Hello",spc,str "World!"
> ,blo 3 [str "Goodbye",spc,str "Space!"]
> ,spc,cat [str "The",spc,str "End"]]) ""
>
> test2 = putStrLn $
> pr 12 (blo 3 [str "Hello",spc,str "World!",spc
> ,blo 3 [str "Goodbye",spc,str "Space!"]
> ,spc,cat [str "The",spc,str "End"]]) ""
>
> test3 = putStrLn $
> pr 12 (blo 3 [str "Hello",spc,str "World!"
> ,blo 3 [str "Goodbye",spc,str "Space!"]
> ,spc,cat [str "The",spc,str "End"]]) ""
>
>
> {-
> *Blocks> test1
> Hello
> World!Goodbye
> Space!
> The
> End
> *Blocks> test2
> Hello World!
> Goodbye
> Space!
> The End
> *Blocks> test3
> Hello
> World!Goodbye
> Space!
> The End
> -}
More information about the Haskell-Cafe
mailing list