Exercise 1

Going through the book myself, I was stuck on how to interpret the requirements of the first exercise. After a bit of searching, I hit upon this approach to the problem.

The idea is that every time you encounter a Line , you first pad out the Doc before the Line .

Barry Allison's posted solution seems to have lost some <> characters in HTML formatting, but if I put them back into the places I guess they're supposed to go, his code seems to work.

The drawback of that solution is that it doesn't pad the last line, so I've modified the implementation to do that:

fill :: Int -> Doc -> Doc fill width x = hcat (init (scanLines 0 [x <> Line])) where scanLines _ [] = [] scanLines col (d:ds) = case d of Empty -> scanLines col ds Char c -> Char c : scanLines (col + 1) ds Text s -> Text s : scanLines (col + length s) ds Line -> Text (padLine (width - col)) : Line : scanLines 0 ds a `Concat` b -> scanLines col (a:b:ds) _ `Union` b -> scanLines col (b:ds) padLine w = replicate w ' '

In order to pad the last line, the fill function first appends a Line to the input, and then calls scanLines . The difference from Barry Allison's solution is that in this implementation, scanLines has the type Int -> [Doc] -> [Doc] . This means that init can be used to throw away the trailing Line , and hcat can finally turn the [Doc] into a Doc .

The drawback of this solution is that it throws away the flattened version of the any Union .

If you modify padLine to replicate the character '#' instead of ' ' , you can see this output:

*PrettyJSON> let value = renderJValue (JObject [("f", JNumber 1), ("q", JBool True)]) *PrettyJSON> putStrLn (pretty 20 (Prettify.fill 30 value)) {"f": 1.0,#################### "q": true##################### }#############################

It's likely that there's a more idiomatic and elegant solution to this exercise, but I'm posting this based on what I've learned so far from reading the book.

Exercise 2

When it came to the second exercise, I took some liberties with interpreting the requirements. As other people have pointed out, both here and elsewhere, the requirements aren't clear.

The following, then, is more of a sketch of solution:

nest :: Int -> Doc -> Doc nest indentation x = indent 0 [x] where indent _ [] = Empty indent nestLevel (d:ds) = case d of Empty -> indent nestLevel ds Char '{' -> padLine nestLevel <> Char '{' <> indent (nestLevel + 1) (Line:ds) Char '}' -> padLine (nestLevel - 1) <> Char '}' <> indent (nestLevel - 1) ds Char '[' -> padLine nestLevel <> Char '[' <> indent (nestLevel + 1) (Line:ds) Char ']' -> padLine (nestLevel - 1) <> Char ']' <> indent (nestLevel - 1) ds Char c -> Char c <> indent nestLevel ds Text s -> Text s <> indent nestLevel ds Line -> padLine nestLevel <> indent nestLevel ds a `Concat` b -> indent nestLevel (a:b:ds) a `Union` b -> indent nestLevel (a:ds) `Union` indent nestLevel (b:ds) padLine nl = Line <> Text (replicate (nl * indentation) ' ')

There are most likely boundary cases, and subtle interactions with the pretty function from the book that could mean that this isn't a complete solution, but at this point, I've already used too many hours on this, and I don't feel that I'm learning Haskell from struggling with unclear requirements.

Here's a sample GHCI interaction that demonstrates how it works:

*PrettyJSON> let value = renderJValue (JObject [("foo", (JObject [("baz", JNumber 123)])), ("bar", JNumber 456)]) *PrettyJSON> putStrLn (pretty 10 (Prettify.nest 4 value)) { "foo": { "baz": 123.0 }, "bar": 456.0 }

As you can tell, there are too many line breaks, but the basic indentation seems to produce some sort of decent-looking nesting.