-
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
Comparison pattern #661
Comments
While I appreciate the suggestion and I've more than once been stymied by the fact that the compiler cannot check for completeness in trivial cases, it's less obvious than it seems. The first thing that comes to mind is transitivity. It is natural to think that But it isn't. While for integers there are corner cases at or around overflow situations (think And then there's the situation of overloading operators, or redefining existing operators, in these cases, transitivity bets are totally off. Another area to look at is that two values can be binary equal, but unequal to each other. Or they can be binary unequal, but equal for the equality operator. And then there's the question of the implementations of I'm not saying it's impossible what you are suggesting, but it really requires rigorous thinking as to what sets of rules are considered complete. And there's one thing you can already do, as you show, and that is to help the compiler by creating complete active patterns (as opposed to partial ones). |
|
@0x53A actually, that would be the inverse. Perhaps "matches are complete but no special test for NaN detected". Which is precisely why this discussion is worthwhile, even if it's only to find out what we, as programmers want to be warned about, and how to reach that deterministically, but also, that we have to be careful what to wish for ;) |
Interesting points, I didn't even think about NaN. That's why I asked this question -- I knew there would be some things I'm missing and some good ideas from the community! :) I agree though in it would probably be best for the compiler to remind you of this case. A minor question comes to mind, though: should the warning be different than a normal "incomplete pattern match exception", so that it is easy to selectively not care about this case if you want to accept the responsibilities? And @abelbraaksma mentions active patterns (though not in the way I was originally thinking -- good stuff). This can pretty much already be implemented in user code, just without infix syntax: // just a quick PoC as this can definitely be improved; i.e. we probably want to know _which_ number the NaN is
let (|LT|EQ|GT|NaN|) (a, b) =
if System.Double.IsNaN a || System.Double.IsNaN b then NaN (a, b)
else if a < b then LT (a, b)
else if a = b then EQ (a, b)
else GT (a, b)
// sample usage:
let printCmp a b =
match a, b with
| LT (a, b) -> sprintf "%f < %f" a b
| EQ (a, b) -> sprintf "%f = %f" a b
| GT (a, b) -> sprintf "%f > %f" a b
| NaN (a, b) -> sprintf "one of (%f, %f) is NaN" a b Perhaps a good option (and would also be much larger in scope) would be to invent infix operator active patterns. That way, these rules wouldn't be hardcoded into and dictated by the compiler, and could be switched out for different rules at will (like how the only thing special about let (|op_LessThan|op_Equality|op_GreaterThan|NaN|) (a, b) =
if System.Double.IsNaN a || System.Double.IsNaN b then NaN (a, b)
else if a < b then op_LessThan (a, b)
else if a = b then op_Equality (a, b)
else op_GreaterThan (a, b) |
Just to mention that the original suggestion has been made before and declined, so we won't be doing it. But I'll leave this open for further discussion |
The language Prolog is pretty much entirely based around this issue. |
@dsyme do you recall the rationale for declining this in the past? |
.net has a standard in this area and we use that. Also, ocaml used integers too |
A standard in this area? Can you explain? |
No idea what he means, but there are so many variations possible for each type... , see my comment. Though in your own code it's trivial to create an active pattern that operates on comparable types, so that's perhaps another reason why they don't want to increase the surface area of core functions. But I'm guessing ;). |
Oh I see, the proposal is to add an active pattern, rather than a new type with cases In any case, I don't think we'll add this to the standard library. The user can define such a pattern if needed |
@dsyme To clarify, this proposal is not just to add an active pattern, but a new language-level pattern: as it stands, If the objection is that language doesn't want to take an opinion on the nuances of the behavior of such a pattern, perhaps a sane modification would be to make this pattern syntactic sugar for a user-definable active pattern, like how the language recognizes the sugar for the |
Oh I see. Sorry for not reading properly again - too hasty. This isn't something we're likely to do - it's a feature that has been considered since pattern matching was invented, and borders on the whole field of constraint solving for tools like Z3 - there is basically no end to the amount of logic power we could pour into checking these kind of constraints. This also means there is is no end to the subtlety the programming team may need to be aware of about how this logic checking power is being used. |
@jwosty If you simply replace So, the downside currently is that you don't have completeness checking. But you can put the three (or four) cases in an active pattern, this will infer that the types must be comparable, and you have what you want to have: completeness checking and generics. You can even create the type to support multiple arguments per case. |
@abelbraaksma yes, that about sums it up. |
Perhaps as an improvement on your code from Apr 12, 2018, you can make it a tad more readable and/or usable with this: let (|LT|EQ|GT|NaN|) (a, b) =
match a, b with
| _ when a > b -> GT
| _ when a = b -> EQ
| _ when a < b -> LT
| _ -> NaN
let f a b =
let print x = printfn "Value %A is %s %A" a x b
match a, b with
| LT -> print "less than"
| EQ -> print "equal to"
| GT -> print "greater than"
| NaN -> print "NaN"
This approach has the advantage that the type is inferred to support anything that is comparable, and treats The Oh, and your original code could become as generic as possible given the requirement by doing this (granted, this isn't obvious): let inline (|Negative|Zero|Positive|NaN|)< ^t when ^t: (static member Zero: ^t) and ^t : comparison> (x: ^t) =
match x with
| x when x = LanguagePrimitives.GenericZero -> Zero
| x when x > LanguagePrimitives.GenericZero -> Positive
| x when x < LanguagePrimitives.GenericZero -> Negative
| _ -> NaN Just my 2p ;) |
Closing as declined |
Title of Suggestion
I propose we add a new first-class pattern that tests comparison on appropriate objects. For example, I'd love to be able to write code like the following, without using getting an incomplete match warning or resorting to wildcards:
I'd love if there were a built-in pattern that accomplishes this. Simply using
| (x > 0) -> ...
would not be possible because it is too restrictive*. So, I present a few possible syntaxes:Whatever the syntax, the feature should be able to be used with more complex patterns of course. Ideally, if possible, it should support comparing bound values to each other, not just literals, as in:
This pattern should support any IComparable, and each of the associated operations --
>
,<
, and '=' (even though the last one is just equivalent to a direct pattern). Perhaps it should also support AND and OR, like so:Though this may make it too complicated for the compiler to check (maybe just AND? or neither of these?)
* It would make it impossible to support comparing two names to each other, so it really has to be a different thing altogether
Pros and Cons
This would be really nice because it adds more compile time safety in these circumstances.
The disadvantages are that this would be yet another pattern for people to keep track of.
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:
The text was updated successfully, but these errors were encountered: