Reputation: 4210
I have the following code (`IgnoreCase is an active pattern I have defined elsewhere):
match myType with
| {Field1 = IgnoreCase "invalid"} -> None
| {Field2 = Some f2
Field3 = Some f3
Field4 = None | Some (0 | 1 | 2)}
-> Some (f2, f3)
| _ -> None
As you can see, Field1
has a blacklisted value and Field4
has whitelisted values (so do Field2
and Field3
in the sense that they must be Some
). IMHO it would look slightly cleaner if I could do all checks in the same case, i.e. do the Field1 = IgnoreCase "invalid"
match together with the other matches using e.g. Field1 <> IgnoreCase "invalid"
, but that particular example does not compile. I know about guards, but that does not seem cleaner than the original solution.
Is it possible to do "negative" (logical NOT) pattern matches in the sense that a value should not match some other value, without using guards?
Upvotes: 2
Views: 630
Reputation: 55195
As far as I know, there's no good way to do this. You can see the supported patterns here and verify that there's no construct for negation. One reason for the lack of such a feature might be that it would interact strangely with other pattern matching features (e.g. what would it mean to introduce an identifier with an as
pattern inside of a negative context?). And this is not something that you could implement in a general fashion yourself via an active pattern, because an active pattern receives a value, not a pattern expression.
Upvotes: 3
Reputation: 10624
You could define an active pattern called Not
:
let (|Not|_|) a b = if a <> b then Some () else None
Then you can use it inside the pattern match:
match myType with
| {Field1 = Not "invalid"
Field2 = Some f2
Field3 = Some f3
Field4 = None | Some (0 | 1 | 2)}
-> Some (f2, f3)
| _ -> None
A limitation of this active pattern is that it must take a literal value, not a sub-pattern. Active patterns aren't composable like the built-in patterns. However, that's also a potential advantage because you can pass it identifiers as values instead of binding a new value to the identifier:
let invalidString = "invalid"
match myType with
| {Field1 = Not invalidString
...
Upvotes: 2
Reputation: 9934
I would use active patterns like this
let (|Check|_|) x =
let = { Field2 = f2; Field3 = f3; Field4 = f4 }
let check =
f4 = None ||
f4 = Some 1 ||
f4 = Some 2 ||
f4 = Some 3
if check then Some (f2, f3) else None
match myType with
| {Field1 = "invalid"} -> None
| Check (x, y) -> Some (x, y)
| _ -> None
Upvotes: 1