[Flesh out the pretty printer to the point of compact rendering Bryan O'Sullivan **20080228080539] { addfile ./examples/ch06/Concat.hs hunk ./en/ch05-fp.xml 1313 - + hunk ./en/ch06-library.xml 21 - "a string" 12345 true null + hunk ./en/ch06-library.xml 26 - The language also allows for the construction of - compound values. Its compound types are the array, an ordered - sequence of values; and the struct, an unordered collection of + The language also supports compound values. Its + compound types are the array&emdash;an ordered sequence of + values&emdash;and the struct&emdash;an unordered collection of hunk ./en/ch06-library.xml 32 - [-3.14, true, null, "a string"] {"numbers": - [1,2,3,4,5], "useful": false} + hunk ./en/ch06-library.xml 145 - ghc -c - SimpleJSON.hs + ghc -c SimpleJSON.hs hunk ./en/ch06-library.xml 190 - ghc -o simple Main.hs - SimpleJSON.o + ghc -o simple Main.hs SimpleJSON.o hunk ./en/ch06-library.xml 338 - During the early stages of our Haskell development - adventure, we have so many new, unfamiliar concepts to keep - track of at one time that it can be a challenge to write code - that compiles at all. + Early on, as we come to grips with Haskell development, we + have so many new, unfamiliar concepts to keep track of at one + time that it can be a challenge to write code that compiles at + all. hunk ./en/ch06-library.xml 681 + Notice that the Doc type describes a tree: the + Concat and Union constructor create an + internal node from two other Doc values, while the + Empty and other simple constructors build + leaves. + hunk ./en/ch06-library.xml 716 - on the left or right has no effect. If we briefly put on our - mathematical hats, we can say that Empty is the - identity under concatenation. Taking the mathematical - perspective has useful practical consequences, and we'll explain - why later. + on the left or right has no effect. This keeps us from bloating + the tree with useless values. hunk ./en/ch06-library.xml 719 - Add a reference to wherever we introduce monoids. + + A mathematical moment hunk ./en/ch06-library.xml 722 - + If we briefly put on our mathematical hats, we can say + that Empty is the identity under concatenation. + Taking the mathematical perspective has useful practical + consequences, and we'll explain why later. + + Add a reference to wherever we introduce + monoids. + + + Our hcat and fsep + functions concatenate a list of Doc values into + one. In , we mentioned that we could + define concatenation for lists using + foldr. + + &Concat.hs:concat; + + Since (<>) is analogous to + (:), and empty to + [], we can write hcat + and fsep as folds, too. + + &Prettify.hs:hcat; + + The definition of fsep depends on + several other functions. + + &Prettify.hs:fsep; + + These take a little explaining. The + softline function should insert a newline + if the current line has become too wide, or a space otherwise. + How can we do this if our Doc type doesn't contain + any information about rendering? Our answer is that every time + we encounter a soft newline, we maintain + two representations of the document, using + the Union constructor. + + &Prettify.hs:group; + + Our flatten function replaces a + Line with a space, turning two lines into one + longer line. + + &Prettify.hs:flatten; + + Notice that we always call flatten on + the left element of a Union. We'll be making use + of this in our rendering functions below. + + + Compact rendering + + Quite often, we want to use a representation for a piece + of data that contains as few characters as possible. For + example, if we're sending JSON data over a network connection, + there's no sense in laying it out nicely: the software on the + far end won't care whether the data is pretty or not, and the + layout will add a lot of overhead. + + For these cases, and because it's a simple piece of code + to start with, we provide a bare-bones compact rendering + function. + + &Prettify.hs:compact; + + In our &case; expression, the branch that matches the + Union constructor ignores the left element, on + which we called flatten. + + We now have enough definitions fleshed out that we ought + to be able to use our compact function in + &ghci;. + + &prettyjson.ghci:compact; + hunk ./examples/ch06/Concat.hs 1 +import Prelude hiding (concat) + +{-- snippet concat --} +concat :: [[a]] -> [a] +concat = foldr (++) [] +{-- /snippet concat --} hunk ./examples/ch06/Prettify.hs 27 - | Line Bool + | Line hunk ./examples/ch06/Prettify.hs 60 -line = Line False +line = Line hunk ./examples/ch06/Prettify.hs 63 +{-- snippet hcat --} hunk ./examples/ch06/Prettify.hs 69 +{-- /snippet hcat --} hunk ./examples/ch06/Prettify.hs 71 +{-- snippet fsep --} hunk ./examples/ch06/Prettify.hs 80 +{-- /snippet fsep --} hunk ./examples/ch06/Prettify.hs 82 +{-- snippet group --} hunk ./examples/ch06/Prettify.hs 84 -group x = Union (flatten x) x - where flatten (Concat x y) = Concat (flatten x) (flatten y) - flatten (Line break) = if break then Empty else Text " " - flatten (Union x y) = flatten x - flatten other = other +group x = flatten x `Union` x +{-- /snippet group --} + +{-- snippet flatten --} +flatten :: Doc -> Doc +flatten (x `Concat` y) = flatten x `Concat` flatten y +flatten Line = Text " " +flatten (x `Union` _) = flatten x +flatten other = other +{-- /snippet flatten --} hunk ./examples/ch06/Prettify.hs 102 -compact :: Int -> Doc -> String -compact _ x = transform [x] +{-- snippet compact --} +compact :: Doc -> String +compact x = transform [x] hunk ./examples/ch06/Prettify.hs 108 - Empty -> transform ds - Char c -> c : transform ds - Text s -> s ++ transform ds - Line _ -> transform ds - Concat a b -> transform (a:b:ds) - Union _ b -> transform (b:ds) + Empty -> transform ds + Char c -> c : transform ds + Text s -> s ++ transform ds + Line -> '\n' : transform ds + a `Concat` b -> transform (a:b:ds) + _ `Union` b -> transform (b:ds) +{-- /snippet compact --} hunk ./examples/ch06/Prettify.hs 117 -pretty w x = best 0 [x] - where best n (d:ds) = +pretty width x = best 0 [x] + where best col (d:ds) = hunk ./examples/ch06/Prettify.hs 120 - Empty -> best n ds - Char c -> c : best (n + 1) ds - Text s -> s ++ best (n + length s) ds - Line _ -> '\n' : best n ds - Concat a b -> best n (a:b:ds) - Union a b -> nicest n (best n (a:ds)) - (best n (b:ds)) + Empty -> best col ds + Char c -> c : best (col + 1) ds + Text s -> s ++ best (col + length s) ds + Line -> '\n' : best col ds + a `Concat` b -> best col (a:b:ds) + a `Union` b -> nicest col (best col (a:ds)) + (best col (b:ds)) hunk ./examples/ch06/Prettify.hs 128 - nicest n a b | min w n `fits` a = a - | otherwise = b + nicest col a b | min width col `fits` a = a + | otherwise = b hunk ./examples/ch06/Prettify.hs 131 - w `fits` "" = True - w `fits` ('\n':_) = True - w `fits` (c:cs) = (w - 1) `fits` cs + w `fits` "" = True + w `fits` ('\n':_) = True + w `fits` (c:cs) = (w - 1) `fits` cs hunk ./examples/ch06/PrettyJSON.hs 11 -import Prettify (Doc, (<>), char, double, fsep, hcat, punctuate, text) +import Prettify (Doc, (<>), char, double, fsep, hcat, punctuate, text, + compact, pretty) hunk ./examples/ch06/prettyjson.ghci 17 +--# compact +let value = jvalue . JObject $ [("f", JNumber 1), ("q", JBool True)] +:type value +putStrLn (compact value) + }