quchen / prettyprinter

A modern, extensible and well-documented prettyprinter.
BSD 2-Clause "Simplified" License
294 stars 36 forks source link

Nested `nest` create undesired space indent #198

Closed TheKK closed 3 years ago

TheKK commented 3 years ago

Example

-- There are 4 spaces instead of 2
Main > putDocW 10 $ nest 2 $ fillSep [nest 2 $ fillSep $ fmap pretty [1..10]]
1 2 3 4 5
    6 7 8
    9 10

From my point of view this result is not expected. I want the first nest 2 only apply on the breaks between its element,and does not affect the break inside its elements. That's more reasonable to me.

It should work like these

Main > putDocW 10 $ nest 2 $ fillSep ["var", "=", "longlonglong"]
var =
  longlonglong

-- Not the actual reault
Main > putDocW 10 $ nest 2 $ fillSep ["var", "=", nest 2 $ fillSep $ fmap pretty [1..10]]
var = 1 2
  3 4 5 6
  7 8 9 10

In my case, the second nest 2 is from other function and it needs to work properly on its own, so removing the second nest 2 isn't an option. One way I found to work is applying nest (-2), it does work but still makes me wondering, is this a bug? Is it expected? Or if there's better way to solve this kind of problem?

sjakobi commented 3 years ago

I don't understand your issue properly yet. What I get is that for

putDocW 10 $ nest 2 $ fillSep ["var", "=", nest 2 $ fillSep $ fmap pretty [1..10]]

you'd like to see

var = 1 2
  3 4 5 6
  7 8 9 10

when in fact it's laid out as

var = 1 2
    3 4 5
    6 7 8
    9 10

To me the actual behaviour seems consistent with the documentation, so I don't think there's a bug.

I want the first nest 2 only apply on the breaks between its element,and does not affect the break inside its elements.

I don't quite understand what you mean by this. Could you change your code like this?

> putDocW 10 $ nest 2 $ fillSep ["var", "=", fillSep $ fmap (nest 2 . pretty) [1..10]]
var = 1 2
  3 4 5 6
  7 8 9 10
TheKK commented 3 years ago

Sorry for my unclear expression. Let me elaborate with a more specific example.

Assume that we have follow Docs and expressions:

let func name args = nest 2 $ name <> parens (fillSep $ punctuate "," $ fmap pretty args)
let assign l r = nest 2 $ fillSep [l, "=", r]

Following results are okay (to me)

putDocW 50 $ func "f" ["x", "yyyyy", "zzz"]
  ==
f(x, yyyyy, zzz)

-- I want func to nest when its argument list is too long
putDocW 10 $ func "f" ["x", "yyyyy", "zzz"]
  ==
f(x,
  yyyyy,
  zzz)

putDocW 50 $ assign "v" "xyz"
  ==
v = xyz

-- I want assign to nest when its lhs or rhs is too long
putDocW 5 $ assign "v" "xyz"
  ==
v =
  xyz

putDocW 50 $ assign "v" $ func "longf" ["x", "y", "z"]
  ==
v = longf(x, y, z)

-- This is fine to me since its indent doesn't leap from 0 to 4. It move from
-- 0, 2 then 4 incrementally.
putDocW 10 $ assign "v" $ func "longf" ["x", "y", "z"]
  ==
v =
  longf(x,
    y, z)

And here comes the issue of mine, the 0 to 4 leap

putDocW 15 $ assign "v" $ func "longf" ["x", "y", "z"]
  ==
v = longf(x, y,
    z)

My intuition tells me that, since we didn't break before "v", before "=" or before "longf(...", it means that the first nest shouldn't apply to the rest of Doc. The reason is simple: the first nest is applied to fillSep ["v", "=", "longf..."] so when we can render "longf" without break in first line, its job should be over and let func "longf" ["x", "y", "z"] handles its own indention.

Maybe my intuition is wrong though. If the indention leap works as expected then I have no more issue with that :)

btw, thanks for your suggestion but this trick doesn't work on my case since the second fillSep can't work on its own.

> putDocW 10 $ nest 2 $ fillSep ["var", "=", fillSep $ fmap (nest 2 . pretty) [1..10]]
var = 1 2
  3 4 5 6
  7 8 9 10

-- I want (nest 2) to apply in this case as well
> putDocW 10 $ fillSep $ fmap (nest 2 . pretty) [1..10]
1 2 3 4 5
6 7 8 9 10
TheKK commented 3 years ago

After I gain more understanding with how pretty works, I have no more issue with the nest behaviour so I will close this issue.

Thank you for your kind suggestion :)