ormolu icon indicating copy to clipboard operation
ormolu copied to clipboard

Formatting of "in" in let bindings

Open GildedHonour opened this issue 4 years ago • 10 comments

Why does it insert one additional whitespace before "in"? How to turn it off, to be 2 ones?

  let a = 1 -- 2 whitespaces
       b = 2
       c = 3
   in a + b + c -- 3 whitespaces

GildedHonour avatar May 01 '20 13:05 GildedHonour

This way the expressions coming after in start at an indentation level that is a multipiler of our indentation step, 2. There is no way to turn it off.

mrkkrp avatar May 01 '20 15:05 mrkkrp

"in" = 3 + 1 whitespace +1st symbol of code = 5th symbol. Not a multiplier of 2.

Strange feature. Where is in Haskell code it also the case -- that "in" has the indentation of 3 whitespaces whereas "let" and everything else remain at 2 ones?

GildedHonour avatar May 01 '20 16:05 GildedHonour

let a = 5
 in a + 1
12345

a starts at 5th column with indentation 4 (1234 bit, 4 characters), because we count columns from 1.

mrkkrp avatar May 01 '20 17:05 mrkkrp

why not add whitespace after "in"?

let a = 5
in  a + 1
12345

GildedHonour avatar May 02 '20 04:05 GildedHonour

@mrkkrp can we reopen this? @GildedHonour is raising a point worth discussing. Putting an extra space before or after the in is certainly unusual. I've never seen that style in any code before. It's also unusual in that every other element of Ormolu style means every line is indented by a multiple of 2 spaces, except for the in in let-bindings.

I understand the justification you give for doing it this way. But there are plenty of other ways to do it, that don't force us into unfortunate exceptions. Furthermore, by your reasoning, if-then-else would have to be indented specially too, so that the indentation level of the expression coming after each keyword is a multiple of 2. To wit,

  1. if the expression after the in is single-line, then what column it starts at is irrelevant. What matters is the indentation of the line, and that should be a multiple of 2 (unlike currently).
  2. If the expression after the in is multi-line, then you don't need to have it start on the same line as in. You can just as well have it start on the next line.

It can be instructive to look at the style that has been found to be effective in languages where people use let bindings far more frequently than in Haskell. These are far more frequent in OCaml, because there is no where construct. They do this:

let x = 2 in
foo x baz

let f 1 = True
    f n = False in
foo 2

But in the multi-line case, the following could work just as well:

let f 1 = True
    f n = False
in
foo 2

or even

let
  f 1 = True
  f n = False
in
foo 2

Having the in on the previous line serves them well because that way they can chain let's. Their style guide includes a further justification: https://ocaml.org/learn/tutorials/guidelines.html#How-to-indent-let-in-constructs.

mboes avatar May 02 '20 07:05 mboes

I never liked this style in OCaml because it is harder to see where the inner expression starts. They are almost forced to use this style because it is the only reasonable way to nest many lets. OCaml programmers cannot introduce a let with many bindings. We can. In fact, nested lets are rare in Haskell, and I think this difference should be taken into account.

Also not a big fan of keeping in on a separate line alone in case of multiline expressions. It somehow looks ugly to me, like a waste of vertical space or something. I think what we have right now is a reasonable compromise.

Stylistic battles are hard and exhausting.

mrkkrp avatar May 02 '20 09:05 mrkkrp

I never liked this style in OCaml because it is harder to see where the inner expression starts.

By inner expression, do you mean whatever comes after the in? That seems orthogonal to whether the in goes on the previous line or not. Some OCaml code is written with the following variation:

let x = 2 in
  foo x

(or even bigger levels of indentation)

Also not a big fan of keeping in on a separate line alone in case of multiline expressions. It somehow looks ugly to me, like a waste of vertical space or something.

Cannot argue with that either way. Haskell code is unusually dense relative to nearly every other language out there, and modern screens are massive. We can surely afford a few more lines here and there. Especially if it means a more predictable style. And all the more so if it's been tried and tested by thousands of projects over many years in another community.

The single space thing is so subtle and hard to see that I hadn't even noticed it. If it's hard to see how to be compliant with a formatter, that can be a problem.

mboes avatar May 02 '20 12:05 mboes

FWIW personally I prefer the current style.

jinwoo avatar May 02 '20 23:05 jinwoo

@jinwoo thank you. We'll talk about that with you sometime, one on one.

GildedHonour avatar May 03 '20 14:05 GildedHonour

I also find this a rather idiosyncratic choice.

let x = 1
in x

is pretty much the only style I've ever seen in Haskell codebases that I've worked on (except those that now use ormolu, of course :p ).

michaelpj avatar Aug 10 '20 14:08 michaelpj