If ordinary functions could be used as patterns it would save having to write trivial active patterns like
let (|NotEmpty|_|) s = Seq.tryPick Some s
and would, hypothetically, allow
let s = seq []
match s with
| Seq.tryPick Some -> ...
| _ -> //empty
This would make functions more reusable, removing the need for "patternizing" functions you want to use with matching:
let f x = if x then Some() else None
let (|F|_|) = f
I know active patterns may be called as functions, so the previous example could be simplified by defining the pattern only. But forgoing the special pattern syntax simplifies this.
What are the reasons for the special syntax?
In the following the active pattern shadows the literal.
[<Literal>]
let X = 1
let (|X|_|) x = if x = 0 then Some() else None
match 0 with //returns true
| X -> true
| _ -> false
Why wouldn't that work for function calls within patterns also?
I found a scenario that would be ambiguous
let zero n = if n = 0 then Some() else None
match 0 with
| zero -> //function call or capture?
This, in my mind, clarifies why an active pattern must begin with an uppercase letter--it makes the intention clearer and makes shadowing, such as in my previous example, much less likely.
Patterns and let-bound variables have different namespaces, which makes sense most of the time given how frequently shadowing occurs and how frequently short identifiers are used. For example, you might define x
on line one of your program and then 200 lines later have match ... with | (x,y) -> x + y
, in which case you almost certainly want x
to be a fresh identifier.
If you want to use arbitrary functions, just use a parameterized active pattern:
let (|Id|_|) f x = f x
match seq [] with
| Id (Seq.tryPick Some) _ -> ...
EDIT
See Name Resolution for Patterns in the spec for details on name resolution. The key is that there is a logical PatItems table which is distinct from the ExprItems table that is used for names in expressions. In the particular case that you added to the edit in your question, the last definition of X
wins, so it's treated as an active pattern in this case (effectively shadowing the literal when X
appears in a pattern).
In addition to issues with name collisions/shadowing, I suspect that there are also ways that allowing a wider range of expressions in patterns would result in ambiguous parses, though I can't think of any off hand.