Promote better awareness of partial functions for new users #1157
Replies: 27 comments
-
Relevant twitter discussion: https://twitter.com/eiriktsarpalis/status/880505955838033920 |
Beta Was this translation helpful? Give feedback.
-
I'm definitely for the warnings + suggestions to use the module functions. Example:
|
Beta Was this translation helpful? Give feedback.
-
@Rickasaurus I don't think totality is necessarily the best criterion for adding such warnings. Take for example |
Beta Was this translation helpful? Give feedback.
-
I was thinking that warnings specifically for an option's |
Beta Was this translation helpful? Give feedback.
-
@cartermp I would try to emphasize pattern matching over |
Beta Was this translation helpful? Give feedback.
-
I agree. Error message should be more like this then:
I also think it's worth not emitting this error if it's guarded properly: if x.IsSome then
printfn "hey look I'm %A" x.Value
else
printfn "I am nothing" Though I suppose it's equally as valid to say that |
Beta Was this translation helpful? Give feedback.
-
@cartermp Why should you warn on I also don't think that we need to go down the route of complex guarded property checks - doing an |
Beta Was this translation helpful? Give feedback.
-
Some data
I'm not defending them :) But I probably wrote most of them, for better or worse, and I generally try fairly hard to avoid them (though not as hard as others, and not as hard as I should). The integrity of the code base has definitely suffered because of their encroachment Note I have no beef with IsNone, IsSome, IsEmpty, nonNil etc. |
Beta Was this translation helpful? Give feedback.
-
@dsyme I would agree that What about array / list indexers? Theoretically those are also unsafe and could be replaced with |
Beta Was this translation helpful? Give feedback.
-
There are others too - List.last etc.
Certainly array indexers represent such a thoroughly well-established form of programming that it would be very hard to constrain that. And there are serious performance issues involved Note that I'm more inclined to an opt-in mechanism, where policy can be applied later in enterprises. I can't justify forcing people to rewrite existing, working codebases to eliminate a new opt-out warning (or else opting out of the warning altogether) Adding "informational" messages to the system may also be reasonable |
Beta Was this translation helpful? Give feedback.
-
Agreed - I'm not suggesting breaking changes or warnings everywhere. And as you say, there's perf considerations (as well as the fact that array indexing is a well-known pattern). Are you thinking along the lines of having essentially a switch on the compiler that turns on a "strict" mode, with more warnings etc.? If so - you might want to be consistent across these calls - things that can't be proven as safe would give a warning? |
Beta Was this translation helpful? Give feedback.
-
@isaacabraham I agree with most of your sentiments, but this is a bit strange:
There are cases where something like |
Beta Was this translation helpful? Give feedback.
-
RE: The problem here as far as I understand it is it's very difficult to check if-else statements because the type system doesn't define that "it's one of these discrete things" in any meaningful way. You could maybe special case option types but how could you do this generally? RE: Indexing Most programmers are familiar with indexing (although most programmers make mistakes with indexing regularly). A language where indexing returned an option would be amazing as long as it handled operations on those types well, but I doubt F# will ever go there. (It's a shame it can be shadowed with a module like with the numeric stuff). |
Beta Was this translation helpful? Give feedback.
-
@Rickasaurus isn't this is the sort of thing Typescript does - it checks to see how some value is being used to determine if it's "safe" to access (wasn't this also something being suggested for C# vNext - safe null checks?). @cartermp I see the difference with those two as with |
Beta Was this translation helpful? Give feedback.
-
@isaacabraham I'm not familiar with it, but I assume it's one of those things that only works some of the time and only with null. If it's broader than that it would be excellent to figure out if it could fit into F# :). |
Beta Was this translation helpful? Give feedback.
-
This almost should be a breaking change. As still rather a newbie, I've been meaning to find the right place to ask: if null and null checking were a "billion dollar mistake", why should there even be List.head and empty list checking? A warning seems an absolute minimum. (Okay that's a rhetorical question and people may have some good reasons. But it still seems to me that eg list operations and empty lists are exactly the same as null checking dangers.) |
Beta Was this translation helpful? Give feedback.
-
(Working with time series simulations it was natural to pattern match in recursive iterative functions. But it was too easy to forget or be complacent in the wind-down, when the series were "supposed to have been" validated already, and the end points were getting recorded.) |
Beta Was this translation helpful? Give feedback.
-
Well, If that specific expression wasn't safe, then we'd have quite a crisis on our hands 😛. An issue here would be large codebases which use this pattern and also have warnings turned into errors. Now we've forced them to either turn off that setting or rewrite parts of the codebase which would otherwise not need any changes. I'm just not too hot on putting a blanket warning on that, even though you're correct in that it mostly shouldn't get used. |
Beta Was this translation helpful? Give feedback.
-
@cartermp you know what I mean when I say unsafe - there's no "requirement" on you to first do an (* compiles *)
let f (x:string option) =
if x.IsSome then
x.Value
else "default"
(* still compiles - oops! *)
let f (x:string option) =
// if x.IsSome then
x.Value
// else "default"
(* compiles *)
let f (x:string option) =
match x with
| Some x ->
x
| None ->
"default"
(* does not compile - good *)
let f (x:string option) =
// match x with
// | Some x ->
x
// | None ->
// "default" |
Beta Was this translation helpful? Give feedback.
-
Right, I agree with everything you've said. But I'm just not too hot on a blanket warning here, especially since folks will build with warnings as errors here. Forcing them to go back and change code which otherwise would not have broken wouldn't make people happy. I'm still not opposed to the idea outright, but as someone who has to take the brunt of such a decision from people, I'm a bit hesitant to do so 😄. |
Beta Was this translation helpful? Give feedback.
-
What about adding such attributes to all partial functions in FSharp.Core? [<Browsable(false)>]
[<Warning(12053, "use pattern matching instead")>]
member this.Value = ... It will not appear in intellisense (and newbies will never discover it :) ) and it will emit a warning. Quick fixes may be added for all the functions. |
Beta Was this translation helpful? Give feedback.
-
I think that's a really good idea @vasily-kirichenko |
Beta Was this translation helpful? Give feedback.
-
@cartermp sure, I get that (and agree, more or less :-)). Where do you draw the line though? |
Beta Was this translation helpful? Give feedback.
-
In my opinion any non-total function which has a good alternative should give the warning: Whether it can be safe is irrelevant (anything can be safe if used correctly, including I think things like Array indexes can often be replaced with pattern matching or sequence operators. But there are some situations where there aren't any good alternatives for array indexing. So I'm on the fence for array indexes. I think having an |
Beta Was this translation helpful? Give feedback.
-
@vasily-kirichenko This is awesome. Even better if the attribute could be exposed for library writers to use. Does the warning attribute work in 3rd party libs? |
Beta Was this translation helpful? Give feedback.
-
Maybe one comment why I think this is pretty useful. I would consider myself a f# expert but I'm using it for quite a lot side projects. And I just spent about an hour looking for a `.head´ function call that caused my async code to crash in an edge case. Though the fact that async didn't preserver a usable stacktrace contributed to it taking so long. But I would really like to have this warning it would have made it very easy to spot the issue. And especially new colleagues that I introduce to f# are using partial functions all over the place because there is little guidance to avoid them. |
Beta Was this translation helpful? Give feedback.
-
Keep in mind that these correctness checks only apply to immutable code. E.g. this code isn't actually correct, even though it performs a check, so the proposed warning would still need to appear here: member val Name with get, set = None
member this.GetName() =
if Option.isSome this.Name then
this.Name.Value // or Option.get, if you please
else "<Unnamed>" ( |
Beta Was this translation helpful? Give feedback.
-
I propose we look for a way to promote better awareness of partial functions (and methods/properties) for new users for F#. In observing many new F# programmers at my company I've found it's much too easy for them to reach for Option.Value, Option.get or List.head with no negative feedback until code review time.
The existing way of approaching this problem in F# is to try to inform up front and code review later.
Pros and Cons
The advantages of making this adjustment to F# are easier on-boarding of new team members.
The disadvantages of making this adjustment to F# are that it might be annoying for seasoned vets.
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Related suggestions: (put links to related suggestions here)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
Beta Was this translation helpful? Give feedback.
All reactions