Formatting docs: Parentheses on separate lines will make code easier to work with, too
- Dominant language
- No language data
- Stars
- 4.8k
- Forks
- 6.1k
- Avg merge
- 15h 21m
- Merged PRs (30d)
- 370
Description
**Note:** I’m not here to discuss some code style that _I_ think _looks_ better. This is only about code being _easy to edit_ and not cause _unnecessary diff._
I’ve read through https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md from start to finish.
Here’s a [quote](https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md#formatting-lists-and-arrays) from that document that I like very much:
> And as with records, declaring the opening and closing brackets [for lists and arrays] on their own line will make moving code around and piping into functions easier.
Yes, yes, yes!
I’d like to see this mentioned for tuples and parentheses too!
This is _almost_ mentioned at [Formatting discriminated unions](https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md#formatting-discriminated-unions):
> Instantiated Discriminated Unions that split across multiple lines should give contained data a new scope with indentation:
>
> ```fsharp
> let tree1 =
> BinaryNode
> (BinaryNode (BinaryValue 1, BinaryValue 2),
> BinaryNode (BinaryValue 3, BinaryValue 4))
> ```
>
> The closing parenthesis can also be on a new line:
>
> ```fsharp
> let tree1 =
> BinaryNode(
> BinaryNode (BinaryValue 1, BinaryValue 2),
> BinaryNode (BinaryValue 3, BinaryValue 4)
> )
> ```
The second variant is always easier to edit!
Here’s an example of formatting I like: [Formatting constructors, static members, and member invocations](https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md#formatting-constructors-static-members-and-member-invocations)
> ```fsharp
> let person =
> new Person(
> argument1,
> argument2
> )
>
> let myRegexMatch =
> Regex.Match(
> "my longer input string with some interesting content in it",
> "myRegexPattern"
> )
>
> let untypedRes =
> checker.ParseFile(
> fileName,
> sourceText,
> parsingOptionsWithDefines
> )
> ```
Nice! The `)` is on its own line.
The [Formatting tuples](https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md#formatting-tuples) section doesn’t mention how to format multiline tuples.
Here’s some real-world code I have (originally posted in https://github.com/fsprojects/fantomas/issues/815#event-4846461855). Compare this:
```fsharp
getUserById userId
|> AsyncResult.mapError HttpGetError
|> AsyncResult.bind (function
| Some user ->
updateUser { user with token = token }
| None ->
runWithLogging
(createUser
{ id = UserId 0
foreignId = contact.id
name = contact.name
token = token }))
```
…with this:
```fsharp
getUserById userId
|> AsyncResult.mapError HttpGetError
|> AsyncResult.bind
(function
| Some user -> updateUser { user with token = token }
| None ->
runWithLogging
(createUser
{
id = UserId 0
foreignId = contact.id
name = contact.name
token = token
}
)
)
```
It’s really difficult when you get `}))` at the end of a line:
- If I want to move `token = token` upwards, it’s easy to move `}))` too and end up with syntax errors.
- If I need to add another argument to `runWithLogging` or `createUser` it’s difficult to see where in `}))` to place a newline and type the new argument.
Here’s another example, with tuples (originally from https://github.com/fsprojects/fantomas/issues/814). Compare this:
```fsharp
let expected =
(Event.Author "john",
[
Event.Revenue
(Event.RevenueId "123",
Event.Match
{
transactionId = 1337
amounts = { total = 2500000L; vat = 625000L }
})
])
```
…with this:
```fsharp
let expected =
(
Event.Author "john",
[
Event.Revenue
(
Event.RevenueId "123",
Event.Match
{
transactionId = 1337
amounts = { total = 2500000L; vat = 625000L }
}
)
]
)
```
It’s much easier to see where the tuples end when they are on their own lines.
In conclusion:
- In my opinion it is always an anti-pattern to put `]})` at the end of lines. Some people might think it looks neat, but for me it’s just a struggle editing. I want to focus on the logic on my program, but end up matching parens. In a program that favors editability, `]})` should always be on their own lines. That also reduces unnecessary diff when you add new lines.
- Just like the formatting docs say it’s OK to do this with `}` (for records) and `]` (for lists and arrays), I like to see this for `)` as well. Then, [Fantomas](https://github.com/fsprojects/fantomas) (the automatic F# code formatter) would accept an option for this. (It already has options for `]` and `}`, but not `)`).
Thanks for reading! F# is a lovely language! ❤️
---
Note that [Formatting record declarations](https://github.com/dotnet/docs/blob/58f221c6e22fda776c444971e18d37a2740f4e86/docs/fsharp/style-guide/formatting.md#formatting-record-declarations) says this:
```fsharp
// OK
type PostalAddress =
{ Address: string
City: string
Zip: string }
member x.ZipAndCity = $"{x.Zip} {x.City}"
// Unusual in F#
type PostalAddress =
{
Address: string
City: string
Zip: string
}
```
I think it’s sad that the second variant is “unusual”! It’s so much easier to add a new field if I don’t need to find the `}` at the end of a line and move it down.
Contributor guide
Assessment
This issue has not been assessed yet.