-
Notifications
You must be signed in to change notification settings - Fork 143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[style-guide] Vanity alignment used when using long case in match block #647
Comments
Hey, I know you mean well, but this is getting a bit tiresome. Looking at the guide, the word "vanity" isn't even in there anymore.
In this specific case, I believe a lot will depend on the positive impact of https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1108-undentation-frenzy.md. So please, do the homework first before opening issues here. This really impacts a lot of codebases if we change this behaviour. Thanks for understanding. |
While I agree that changes to the default formatting should be carefully considered I would not like to see this kind of things added to the style guide. There are probably hundreds of rare formatting corner cases and if all of them are added to the style guide then it simply gets too long in my opinion. If it is too long it does not work as learning material about how F# code should be formatted. |
Thanks, that's what was exactly my thinking when filing all this bugs, given that vanity-alignment was already discouraged in the guide, as a general principle. Even if the word "vanity" is not used anymore, I think the principle has still remained, if not many code samples. |
This totally isn't one, this involves SynPat.LongIdent which has different offset rules in F# 5 than expressions.
If Fantomas implements the style guide correctly, there is literally zero learning curve to it. To reiterate the impact of this is way too large not to have been justified up front in the style guide. So either you open an issue in the MS docs repository or I close this one. |
Thanks for the clarification.
I still think that the style guide should not become a Fantomas specification covering everything to the smallest detail. There are scenarios and reasons not to use Fantomas and thus having a simple and easy to read style guide is helpful. Though I also think that deciding about changes to the default formatting based only on a discussion in GitHub issue isn't necessarily the optimal way. |
@dsyme do you mind if I ask you your opinion on this? Let me know if you agree with the expected result here. FYI my company has implemented a fix for this in our internal fantomas fork and this is the result as an example of the improvement applied in real code: https://gist.github.com/knocte/5ccb3101f199f041fc6381f47d9eed65 As you can see, it not only fixes the vanity alignment, it also mitigates excessive horizontal deviation to the right side. If this looks ok, I will create PR first for the style guide, and later I'll propose our patch as a PR. |
I would have thought the default result should at least align with the deafult formatting of the expression that corresponds to the patternm e.g. SomeVeryLongMatchCase(
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890) That may mean vanity alignment, which you won't enjoy, or else this: match foo with
| SomeVeryLongMatchCase(
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890) ->
bar()
| _ -> () |
As of F# 6 this is valid as well: match foo with
| SomeVeryLongMatchCase(
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890,
1234567890
) ->
bar()
| _ -> () The nice thing is that it would match the behaviour of regular function applications (example). |
Yes, that's what I meant actually, thanks |
Actually, my initial proposal matches also the current behaviour of fantomas, but not for function applications but for functions that receive tuples (which matches more with what we're talking about here). Example here. This solution would be compatible with older versions of F# (and in fact, my team cannot upgrade to v6.0 yet :( ). |
Interestingly enough, this is now also valid code: module M =
let longFunctionWithLongTupleParameter(
aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
) =
() I understand the need of being compatible with older versions yet at the same not make use of FS-1108 feels like a wasted opportunity as well. The broader question would be: "What F# versions should the style guide support?". |
We should support the latest major version, and perhaps adding notes to the style guide for the previous major version when we remember to. The rest of the docs also do this |
TBH I don't mind if no notes are added about previous versions in the style guide, however, can we still have a flag for fantomas that tries to be more conservative to still support older versions? Something like |
I don't think Fantomas should carry the burden of older versions. If you are using an older language version, you should use an older Fantomas version that is compatible. Any development on Fantomas side where F# 6 relaxations to the indentation rules are used should probably be part of the next major version. PS: you don't necessarily need to target the .NET 6 runtime to use F# 6 features. And NETCore 3.1 LTS ends in December 2022 (source). So, I think we can be a little easy going here. |
It's not about that. F#6 hasn't landed in Debian yet. |
On the style-guide question - I will update the style guide to indicate that multi-line patterns should be formatted in the same say as multi-line applications, like this. Does that seem reasonable? It's possible that due to differences in indentation-aware processing there may be some cases where this is not technically possible - do we know of any with F# 6? |
Nothing comes to mind here, I believe this seems reasonable. |
Another interesting case here would be named fields in a pattern match. Consider: [<Test>]
let ``SynTypeDefn with member with set/get`` () =
let parseResults =
getParseResults
"""
type A() =
member this.Z with set (_:int):unit = () and get():int = 1
"""
match parseResults with
| ParsedInput.ImplFile (ParsedImplFileInput (modules = [ SynModuleOrNamespace.SynModuleOrNamespace(decls = [
SynModuleDecl.Types(
typeDefns = [ SynTypeDefn(typeRepr = SynTypeDefnRepr.ObjectModel(members = [
SynMemberDefn.ImplicitCtor _
SynMemberDefn.GetSetMember(Some (SynBinding(headPat = SynPat.LongIdent(extraId = Some getIdent))),
Some (SynBinding(headPat = SynPat.LongIdent(extraId = Some setIdent))),
m,
{ WithKeyword = mWith
GetKeyword = Some mGet
AndKeyword = Some mAnd
SetKeyword = Some mSet })
])) ]
)
]) ])) ->
Assert.AreEqual("get", getIdent.idText)
Assert.AreEqual("set", setIdent.idText)
assertRange (3, 18) (3, 22) mWith
assertRange (3, 23) (3, 26) mSet
assertRange (3, 23) (3, 26) setIdent.idRange
assertRange (3, 45) (3, 48) mAnd
assertRange (3, 49) (3, 52) mGet
assertRange (3, 49) (3, 52) getIdent.idRange
assertRange (3, 4) (3, 62) m
| _ -> Assert.Fail "Could not get valid AST" This isn't formatted all the nicely today by Fantomas. (sample) Maybe the following makes sense: match x with
| Y (z = patZ) -> ()
Y (z = patZ)
Y (z =
patZ
)
Y (
z = patZ // patZ is short
a =
patA // patA is long
) Another interesting question about the indentation, in general, is if it should be relative to the start of the pattern of the bar of the match clause. match x with
| Y (
... // column is 4
) // notice that the closing `)` should at least be 1 position further than the `|`
| Z (
... // column is 6 because `Z` started at 2
) // notice that the closing `)` should at least be 1 position further than the `|`
|
Hello @dsyme, I made a prototype of the ideas above at fsprojects/fantomas#2645. |
Looking at the tests it looks great! :) |
@dsyme I added some more tests in fsprojects/fantomas@5a7b75f It can grow a little in the wild: dotnet/fsharp@d048025 |
@nojaf Ouch, yes, that is long. Sigh. Still I think it's the right decision. People just have to use active patterns to control this complexity |
This cascading is really problematic isn't it. Hmmm..... https://github.com/dotnet/fsharp/commit/d0480259b450a9d8e398346a12e93be4961343d6#diff-6fb95f66781ae87d118ad76ae0d[…]88674bea9c7401fcb6282b6R125-R137 |
@dsyme I still need to chew on this some more, as these multiline patterns are a fascinating space. I stumbled upon this example this morning: match x with
| [| Y(itemOne =
OhSomeActivePatternThing(
a, b
)
)
S(
one,
two,
three,
four,
five
) |] -> () Imagine that the I like the proposed formatting here, as every indent is based on the You could move everything inside the array to the next line/indent: match x with
| [|
Y(itemOne =
OhSomeActivePatternThing(
a, b
)
)
S(
one,
two,
three,
four,
five
)
|] -> () On a side note, I do think patterns inside a match clause should have slightly different rules, as opposed to perfectly mimicking what would happen inside applications. For example, if an array/list pattern is the sole child of a long ident application, using Stroustrup does make sense to keep it short: match parseResults with
| ModuleName.PatternName([
SomethingElse(
a,
// comment
b,
c
) ]) ->
assertRange (2, 21) (2, 22) mSlash
assertRange (2, 21) (2, 29) mTuple
| _ -> Assert.Fail $"Could not get valid AST, got {parseResults}" The same goes for a single named pattern ( Yet, I'm not convinced this should be the case for every single scenario where you have an array/list/record somewhere in a pattern. match x with
| [
[|
Foo
Bar
|]
[|
Foo
Barry
|]
] ->
() |
Another thing to note is that I'm using the |
In the linked real-world example - everything looks ok. I get that it's not identical to expressions though I can't quite rationalize why. For arrays:
This feels more natural to me. |
Issue created from fantomas-online
Code
Result
Expected Result
Extra information
Options
Fantomas 4.6 branch at 10/24/2021 17:25:41 - 3032b536608ba8f2b37cc261230747fdb06c73ed
Default Fantomas configuration
Did you know that you can ignore files when formatting from fantomas-tool or the FAKE targets by using a .fantomasignore file?
The text was updated successfully, but these errors were encountered: