tweag / topiary

https://topiary.tweag.io/
MIT License
575 stars 29 forks source link

More dangling functions #660

Closed Niols closed 2 months ago

Niols commented 11 months ago

Is your feature request related to a problem? Please describe.

Topiary's default OCaml formatting is very aerated (especially in multi-line mode), which I enjoy quite a lot. However, sometimes, I find it too aerated and I wished some things stayed on the same line. This is for instance the case with functions (fun x -> and function). I feel we could often opt for a slightly more compact style by making functions dangle at the end of the previous line. We currently do that specifically in the case of let . = fun . -> (and function) but I would maybe like to see that in other situations.

Describe the solution you'd like

Here are few examples of how I would format things and how Topiary actually formats them right now. I am not necessarily super attached to all of those, the goal is more to start a conversation on this.

A.

My inputTopiary's output
```ocaml let foo x = fun y -> zzzzzzzzzz ``` ```ocaml let foo x = fun y -> zzzzzzzzzz ```

This is the case that Topiary already handles, although I would prefer it with only one indentation level.

B.

My inputTopiary's output
```ocaml let () = foo @@ fun y -> zzzzzzzzzz ``` or maybe: ```ocaml let () = foo @@ fun y -> zzzzzzzzzz ``` ```ocaml let () = foo @@ fun y -> zzzzzzzzzz ```

I would use the first one. My current editor gives no indentation level then, but I can see how we would prefer one, even if chaining such operators would then give a funny look:

foo @@ fun x ->
  bar @@ fun y ->
    baz @@ fun z ->
      vvvvvvvv

Not that bad, actually. Still, This would be somewhat more compact than Topiary's output and would lead to less indentation levels. Where to put the @@ operators is somewhat covered in https://github.com/tweag/topiary/issues/657. No matter where it goes, I think I'd prefer having fun . -> dangle onto it.

C.

My inputTopiary's output
```ocaml let () = foo >>= fun y -> zzzzzzzzzz ``` ```ocaml let () = foo >>= fun y -> zzzzzzzzzz ```

This is actually not a bad output. I think we probably want to add indentation to this operator, but I'm not sure. Again, positioning of the >>= operator is covered in https://github.com/tweag/topiary/issues/657. I am not sure I understand why the output is like this; is it because the operator ends with an =?

D.

My inputTopiary's output
```ocaml let () = foo x (fun y -> zzzzzzzzzz) ``` or ```ocaml let () = foo x (fun y -> zzzzzzzzzz ) ``` ```ocaml let () = foo x ( fun y -> zzzzzzzzzz ) ```

This is probably the one that annoys me the most. I see where it comes from, and it sometimes makes sense when the lambda is a very big function, but I feel that most of the time this is too much. I would say that the style

(fun y ->
   zzzzzzzzzz)

is more common but I suppose it doesn't fit so well in our aerated style and it also behaves a bit less well with Git diffs, so maybe

(fun y ->
   zzzzzzzzzz
)

is better? I have similar considerations about function but this issue is getting big so maybe in another comment.

Describe alternatives you've considered

The current style is not bad, frankly, and I use it in a fair amount of projects. For some projects of mine, though, I have tried adding it, and really struggled with the result, especially for B. and D. Maybe I will share a snippet of an actual piece of code that I am not happy with.

Additional context

Related to https://github.com/tweag/topiary/issues/657 Related to an incoming issue on dangling tuples/lists/arrays. I know we have discussed similar things at various points, but I am not sure where. Maybe we should link those here.

Niols commented 11 months ago

A.

My inputTopiary's output
```ocaml let foo x = function | y -> zzzzzzzzzz | u -> vvvvvvvv ``` ```ocaml let foo x = function | y -> zzzzzzzzzz | u -> vvvvvvvv ```

B.

My inputTopiary's output
```ocaml let () = foo x @@ function | y -> zzzzzzzzzz | u -> vvvvvvvv ``` or ```ocaml let () = foo x @@ function | y -> zzzzzzzzzz | u -> vvvvvvvv ``` ```ocaml let () = foo x @@ function | y -> zzzzzzzzzz | u -> vvvvvvvv ```

Not bad, actually, could keep that, although I'd prefer my first snippet.

C.

My inputTopiary's output
```ocaml let () = foo x >>= function | y -> zzzzzzzzzz | u -> vvvvvvvv ``` or ```ocaml let () = foo x >>= function | y -> zzzzzzzzzz | u -> vvvvvvvv ``` ```ocaml let () = foo x >>= function | y -> zzzzzzzzzz | u -> vvvvvvvv ```

Also not bad. Would want some indentation of the cases at least, probably.

D.

My inputTopiary's output
```ocaml let () = foo x (function | y -> zzzzzzzzzz | u -> vvvvvvvvv) ``` or ```ocaml let () = foo x (function | y -> zzzzzzzzzz | u -> vvvvvvvvv ) ``` ```ocaml let () = foo x ( function | y -> zzzzzzzzzz | u -> vvvvvvvvv ) ```
Niols commented 11 months ago

@aspiwack You might have an opinion on those things. The tree sitter grammar maintainer might also have one.

Niols commented 8 months ago

I realised today that Ormolu lets you chose whether to dangle a do: it will leave unchanged the two following pieces of code:

f = do
  do_something
f =
  do 
    do_something

Maybe that's a way to go?