-
Notifications
You must be signed in to change notification settings - Fork 6k
Description
The following is a request to change the F# style guidelines.
In section Formatting white space, subsection Place parameters on a new line for long definitions, the style guide says:
If you have a very long function definition, place the parameters on new lines and indent them to match the indentation level of the subsequent parameter.
module M = let LongFunctionWithLotsOfParameters(aVeryLongParam: AVeryLongTypeThatYouNeedToUse) (aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse) (aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse) = // ... the body of the method follows
As mentioned in fsprojects/fantomas#657, this kind of indentation (AFAIK called "vanity indentation") has several drawbacks:
- Important code moved far to the right
- There is little space left for the actual code
- Refactoring names breaks the alignment (even if using tooling like Fantomas to auto-format code, the tooling may need to be explicitly invoked on all changed files, and AFAIK renames that extend names may cause compiler warnings)
- There is an unseemly gap on the left-hand side (personal opinion)
I would format the above example like this:
module M =
let LongFunctionWithLotsOfParameters
(aVeryLongParam: AVeryLongTypeThatYouNeedToUse)
(aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse)
(aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse)
=
// ... the body of the method follows
This has none of the mentioned drawbacks.
As seen in the linked section, this also goes for the recommendations for members and constructors.
Another section which has the same problem is Formatting copy-and-update record expressions, which has this example:
let newState = { state with F = Some { F1 = 0 F2 = "" } }
Notice how the indentation of the F
value is sensitive to the name of F
:
let newState =
{
state with
AGorillaHoldingTheBananaAndTheEntireJungle = Some {
F1 = 0
F2 = ""
}
}
A better approach would be:
let newState =
{
state with
AGorillaHoldingTheBananaAndTheEntireJungle =
Some {
F1 = 0
F2 = ""
}
}
Conversely, in the section Formatting discriminated unions (unrelated to explicit whitespace recommendations), my preferred approach is demonstrated:
let tree1 = BinaryNode (BinaryNode(BinaryValue 1, BinaryValue 2), BinaryNode(BinaryValue 3, BinaryValue 4))
Furthermore, in the section Formatting pipeline operators, the non-vanity indentation kind is also shown (though vanity indentation would have |>
aligned three more spaces to the right):
// Preferred approach let methods2 = System.AppDomain.CurrentDomain.GetAssemblies() |> List.ofArray |> List.map (fun assm -> assm.GetTypes()) |> Array.concat |> List.ofArray |> List.map (fun t -> t.GetMethods()) |> Array.concat // Not OK let methods2 = System.AppDomain.CurrentDomain.GetAssemblies() |> List.ofArray |> List.map (fun assm -> assm.GetTypes()) |> Array.concat |> List.ofArray |> List.map (fun t -> t.GetMethods()) |> Array.concat
In light of these counter-examples, one could argue that changing recommending non-vanity indentation would make the style guide more consistent.
To fix this issue, I suggest the following:
- In the examples above, use the non-vanity kind of indentation
- Add a separate sub-section under "Formatting white space" explicitly stating that the non-vanity kind of indentation is preferred over vanity indentation for the aforementioned reasons. This section could, among other examples, show mutable setters (Don't indent too far fsprojects/fantomas#659) and the reverse pipeline operator (F# style guidelines: no advice given for reverse pipeline operator #21459).
If this is acceptable, I can help create a PR if desired, but I have no personal need to put my name on this change and really just want it changed regardless of who does the work.