-
Notifications
You must be signed in to change notification settings - Fork 21
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
Generate F# 9-style Is* property for single case discriminated union #1394
Comments
Not sure how I feel about this. One odd thing is that you can't polyfill it yourself if you want: type U =
| A
member this.IsA = match this with A -> true > type U =
- | A
- member this.IsA = match this with A -> true;;
member this.IsA = match this with A -> true;;
----------------^^^
stdin(3,17): error FS0023: The member 'IsA' cannot be defined because the name 'IsA' clashes with the default augmentation of the union case 'A' in this type or module I wonder if that is by design. |
It looks like the original implementation emitted this, and it was later changed not to: fsharp/fslang-design#517 (comment) The RFC itself does not mention it: https://github.com/fsharp/fslang-design/blob/3a46bc9342f4c2c38d09617b4d95909697e7b9d6/RFCs/FS-1079-union-properties-visible.md |
Interesting. Doesn't seem like there was a ton of discussion involved - just "this feels weird" and then it gets removed. I think we should revisit that. Having it missing for single case unions feels like a "gotcha". But that is also good news in that the exact place to implement this is straightforward. |
To be fair, @T-Gro did mention the main reason why:
and, in dotnet/fsharp#16571,
I.e., the F# compiler already did generate But the compiler did not generate an |
Yes, that makes sense. Then this could probably be added as a con to implementing this.
I do think the consistency it would bring would be worth the effort. |
Not generating the warning in the described case is doable, that was definitely an oversight and mismatch in codegen vs. warning. I must admit I do not fully see the motivation for practical F# programs. let workWithImage Image = 42
let workWithImages imgList =
match imgList with
| Image :: _rest = 42
| [] -> 2112 |
Regularity is good so this should be added. // This code is OK and remains OK when more shape cases are added.
type Shape = | Rectangle of Dimensions
let countRectangles(shapes: Shape list) =
shapes |> List.countWhere(s -> s.IsRectangle)
But you don't need big use cases to justify making the langauge more regular. Even if there were no use case it should be there! |
If you would have hand-written logic for .IsRectangle, you could amend it if/once future extension arrives. I am the kind of person who would rather get a warning and adjust logic to new requirements case by case. Rather then having no new warnings, but wrong business logic caused by pre-existing usage of .IsRectangle . (because once new requirements were added, counting rectangles might had to include squares as well). But you are right that this equally applies to any number of cases, not only size=1. Usage for size=1 just makes it apparent that the intention is to prepare for unknown future/possible requirements, which I guess is the part which feels wrong about this. |
Yes. YAGNI. |
Yes it does make that apparent, as it should as this is a definition of a DU aka discriminated union. So it feels wrong that the discrimination scaffolding isn't there, including
And yes to YAGNI - sometimes discriminated unions shouldn't be used but instead the underlying data type or a simpler non-discriminated non-union wrapper used, for situations in which it's not expected that cases will be added. But this doesn't mean that it's never useful to start with the possibility of discrimination and so there shouldn't be a restriction on DUs that they should have at least 2 cases. E.g. I might know I am going to write more Shapes, and I might want my serialization/deserialization of Shapes to conform to a DU structure so that it doesn't change when adding more cases. Given that we are not going to ban DUs with one case, ensuring that they are treated as DUs and not something special is important for consistency. |
Disagree here, but I think you put it well:
Some additional context might be helpful - I am a relative newcomer to F#, I've only just recently started writing meaningful amounts of F# code. When I saw the Is* feature as added in F# 9, I immediately had an intuition as to how it should behave based on how the feature was advertised. Namely:
let canSendEmailTo person =
match person.contact with
| Email _ -> true
| _ -> false
let canSendEmailTo person =
person.contact.IsEmail Nothing in this presentation makes me think "instead you can write this, but only in the case of a DU with at least 2 cases" It's a small inconsistency, sure. It's also one that, if fixed, arguably might not necessarily benefit a vast number of use cases. But it's an inconsistency nonetheless. And in my opinion, small inconsistencies are the ones that are the most off-putting for me as a user of a language. |
I propose we generate an Is* boolean property for single case unions, as F# 9 does today for multi-case unions.
The existing way of approaching this problem in F# is to add a throwaway associated value and pattern match, like so:
Pros and Cons
The advantages of making this adjustment to F# are that the property is generated for all cases of a DU, not just when there are multiple. While in theory the Is property is redundant for that case since it is guaranteed to be true, this enhancement improves the behavior for "planning for future cases" by allowing to use the same syntax, instead of having to do a hacky workaround just for the single case scenario
The disadvantages of making this adjustment to F# are that perhaps this is a small thing and maybe the juice isn't worth the squeeze just for this extra consistency?
Extra information
Estimated cost (XS, S, M, L, XL, XXL): S to M
Affidavit (please submit!)
Please tick these items by placing a cross in the box:
Please tick all that apply:
For Readers
If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.
The text was updated successfully, but these errors were encountered: