Description
I propose we add an UnknownEnum
pattern that matches undefined enum values.
For example, consider:
type IsWaterFlowing = WaterFlowing = 0 | WaterNotFlowing = 1
match someWebAPI arguments with
| IsWaterFlowing.WaterFlowing -> "Water is flowing"
| IsWaterFlowing.WaterNotFlowing -> "Water is not flowing"
// FS0104: Enums may take values outside known cases. For example, the value 'enum<_> (0)' may indicate a case not covered by the pattern(s).
The existing way of approaching this problem in F# is adding a wildcard case, but we lose the ability to track new additions:
// New case added!
type IsWaterFlowing = WaterFlowing = 0 | WaterNotFlowing = 1 | WaterPartiallyFlowing = 2
match someWebAPI arguments with
| IsWaterFlowing.WaterFlowing -> "Water is flowing"
| IsWaterFlowing.WaterNotFlowing -> "Water is not flowing"
| _ -> "Unknown" // But no warning is reported here...
By having an UnknownEnum
pattern, we can preserve the ability to track new cases while handling other undefined cases:
// New case added!
type IsWaterFlowing = WaterFlowing = 0 | WaterNotFlowing = 1 | WaterPartiallyFlowing = 2
match someWebAPI arguments with
| IsWaterFlowing.WaterFlowing -> "Water is flowing"
| IsWaterFlowing.WaterNotFlowing -> "Water is not flowing"
| UnknownEnum x -> sprintf "Unknown enum value: %d" x
// UnknownEnum deconstructs the underlying value to enable better logging
// FS0025: Incomplete pattern matches on this expression. For example, the value 'IsWaterFlowing.WaterPartiallyFlowing' may indicate a case not covered by the pattern(s).
match someWebAPI arguments with
| IsWaterFlowing.WaterFlowing -> "Water is flowing"
| IsWaterFlowing.WaterNotFlowing -> "Water is not flowing"
| IsWaterFlowing.WaterPartiallyFlowing -> "Water is partially flowing"
| UnknownEnum x -> sprintf "Unknown enum value: %d" x
Pros and Cons
The advantages of making this adjustment to F# are
- Much easier discovery of bugs by encouraging both matching all existing cases of an enum and matching unknown cases of enums.
- Development speed - everywhere the change enum is used, a warning is reported. Developers do not have to look through lots of lines of code to handle new cases.
- Less crashes due to match failures.
The disadvantage of making this adjustment to F# is the introduction of an hardcoded built-in pattern that influences flow analysis which needs to be learnt. However, the upcoming NotNull
pattern also falls into this bucket, so this category of patterns is already being introduced into F#.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Related suggestions: No
Affidavit (please submit!)
Please tick this by placing a cross in the box:
- This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
- I have searched both open and closed suggestions on this site and believe this is not a duplicate
- This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.
Please tick all that apply:
- This is not a breaking change to the F# language design
- I or my company would be willing to help implement and/or test this