tweag / ormolu

A formatter for Haskell source code
https://ormolu-live.tweag.io
Other
958 stars 83 forks source link

Does ormolu have a concept of max line length? #508

Closed ghorn closed 4 years ago

ghorn commented 4 years ago

We're switching a large work code base to ormolu and finding that ormolu doesn't seem to have a concept of maximum line length. We are having to disable our line length checker because for some modules we can't get ormolu to produce code that short enough.

Is this on the project's radar? Is it an intentional decision to not address line length?

mboes commented 4 years ago

It's an intentional decision. From the README:

Let some whitespace be programmable. [...] This makes the implementation simpler and leaves some control to the user while still guaranteeing that the formatted code is stylistically consistent.

If we had a line length limit, we would have to decide which one. We would have to do something in case the line exceeds the limit, like choose where in the line to break. That might in turn be not sufficient so then we have to break again. The end result will likely look pretty ugly, unless we use elaborate heuristics about how to do good line breaking. That's what Brittany does. The design choice in Ormolu is to have a formatter that's simple to hack on, that leaves line breaking to the programmer.

It would be interesting to see where Ormolu increases the line lengths of the input. I would expect Ormolu to rarely increase line lengths, because if you broke an expression into multiple lines, Ormolu will respect that. If the expression holds on one line, it will not change that - but in that case the premise is that the original expression held on one line within the length limit to start with.

peddie commented 4 years ago

For a simple example of increasing line length, Ormolu changes

    _ <- primFun (PrimWord8 tag) -- count tag   
    -- count maximum number of each type in the union
    tell (maxUnionConLengths cunion :: Arrays ArrayCount)

into

    _ <- primFun (PrimWord8 tag) -- count tag   
          -- count maximum number of each type in the union
    tell (maxUnionConLengths cunion :: Arrays ArrayCount)

The comment now doesn't seem to be aligned with anything in particular.

Speaking generally, increasing indentation can cause a line-length violation. This becomes more likely if the line is nested within a few constructs (e.g. let inside do inside a function), all of whose indentation Ormolu increases.

peddie commented 4 years ago

Here are two indentation increases in code that are less obvious (to me, at least) than simply padding out everything in a where- or let-clause:

            err           <- unsafeNewNamed
              "err"
              (CTypeBackdoor "CborError")
              (cborEncoderCreateArray
                (encoder, TakeAddress array_encoder, LiteralInt 1 :+ numFields_)
              )

is Ormolified (?) to

            err <-
              unsafeNewNamed
                "err"
                (CTypeBackdoor "CborError")
                ( cborEncoderCreateArray
                    (encoder, TakeAddress array_encoder, LiteralInt 1 :+ numFields_)
                )

so that

ghorn commented 4 years ago

I am OK with ormolu not having a concept of line length, as long as ormolu is compatible with line length limits with some user finessing. An artificial example would be if ormolu always took multi-line type signatures and put them on one line, then there would be no way to satisfy a length limit for large type signatures.

Would it be considered a bug if ormolu combined multi-line expressions into one long line, for example?

mboes commented 4 years ago

Would it be considered a bug if ormolu combined multi-line expressions into one long line, for example?

I'm tempted to say that yes, if this happens, it's arguably a bug. I'm not sure to what extent Ormolu currently sticks to this. But there ought to be a very simple rule for the benefit of the programmer. Something like: if the input is single line, the output is always single line, and if the output is multi-line, the output is always multi-line (although the line breaks might be in different places).

mrkkrp commented 4 years ago

I concur, yet I can also imagine that in same special cases when such combining of lines happens and the result is still fitting on a line and look reasonable, it is OK. Maybe the original input was too "spread" and Ormolu just did performed the necessary normalization.

ghorn commented 4 years ago

when such combining of lines happens and the result is still fitting on a line and look reasonable

How do you know if it fits on a line if you don't know low long a line is?

mboes commented 4 years ago

@mrkkrp do you have a concrete example where this might occur?

mrkkrp commented 4 years ago

For example, this

class
  Foo
  x
  where
  foo :: Int

is currently transformed into

class
  Foo
    x where
  foo :: Int

Which deletes the line break before where. I think it is still OK though.

But come think of it, in this particular case there should be a newline before where I think.

mboes commented 4 years ago

Agreed.

mrkkrp commented 4 years ago

where placement was fixed in #510.

mrkkrp commented 4 years ago

I think this can be closed now.