Question

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?

EDIT

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?

EDIT 2

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.

Was it helpful?

Solution

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.

OTHER TIPS

One way to answer the question is to say that active patterns (as a special syntactic category) make it possible to use the same name for constructing value in expression and deconstructing it in a pattern.

For example, let's say that we're using a type Info to represent some information:

type Info = I of string * int

You can write two functions to construct and destruct values of this type:

val constructInfo : string * int -> Info
val destructInfo  : Info -> string * int

The two functions are trivial to implement in this case, but the interesting point is that their type signatures are dual. Construction takes values and creates our (abstract) type and destruction takes the type and returns individual values.

Using active patterns, we can use the same name, Info, for both of the purposes. This makes it consistent with the rest of the language - for example x::xs and (a, b) are both constructors and patterns. The F# library does similar thing for F# quotations (i.e. Lambda is a both pattern and constructor for type Expr.

So, instead of writing construct and destruct functions, we can define a function and a pattern:

let Info (a, b) = I (a, b)
let (|Info|) (I (a, b)) = (a, b)

The result is that the same syntax, Info(a, b), can appear both as a pattern and expression.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top