F# 为什么活动模式需要特殊语法?

F# 为什么活动模式需要特殊语法?,f#,pattern-matching,active-pattern,F#,Pattern Matching,Active Pattern,如果可以将普通函数用作模式,那么就不必编写诸如 let (|NotEmpty|_|) s = Seq.tryPick Some s 并且,假设允许 let s = seq [] match s with | Seq.tryPick Some -> ... | _ -> //empty 这将使函数更具可重用性,不再需要将要用于匹配的函数“模式化”: let f x = if x then Some() else None let (|F|_|) = f 我知道活动模式可以被称为函

如果可以将普通函数用作模式,那么就不必编写诸如

let (|NotEmpty|_|) s = Seq.tryPick Some s
并且,假设允许

let s = seq []
match s with
| Seq.tryPick Some -> ...
| _ -> //empty
这将使函数更具可重用性,不再需要将要用于匹配的函数“模式化”:

let f x = if x then Some() else None
let (|F|_|) = f
我知道活动模式可以被称为函数,所以前面的示例可以通过只定义模式来简化。但是放弃特殊模式语法简化了这一点

特殊语法的原因是什么

编辑 在下面的示例中,活动模式会对文字进行阴影处理

[<Literal>]
let X = 1
let (|X|_|) x = if x = 0 then Some() else None

match 0 with //returns true
| X -> true
| _ -> false

在我看来,这澄清了为什么一个活动模式必须以大写字母开头——它使意图更加清晰,并使阴影(如我前面的示例中)的可能性大大降低。

模式并让绑定变量具有不同的名称空间,考虑到阴影出现的频率和使用短标识符的频率,这在大多数情况下都是有意义的。例如,您可以在程序的第一行定义
x
,然后在200行之后进行
匹配。。。使用|(x,y)->x+y
,在这种情况下,您几乎肯定希望
x
成为新的标识符

如果要使用任意函数,只需使用参数化活动模式:

let (|Id|_|) f x = f x

match seq [] with
| Id (Seq.tryPick Some) _ -> ...
编辑

有关名称解析的详细信息,请参见规范中的。关键是有一个逻辑PatItems表,它不同于表达式中用于名称的ExprItems表。在您添加到问题编辑中的特定情况下,
X
的最后一个定义获胜,因此在这种情况下它被视为活动模式(当模式中出现
X
时,有效地隐藏文字)


除了名称冲突/阴影问题之外,我怀疑还有一些方法允许模式中的表达式范围更广,这会导致不明确的解析,尽管我不能马上想到任何方法。

模式和让绑定变量具有不同的名称空间,考虑到阴影出现的频率和使用短标识符的频率,这在大多数情况下都是有意义的。例如,您可以在程序的第一行定义
x
,然后在200行之后进行
匹配。。。使用|(x,y)->x+y
,在这种情况下,您几乎肯定希望
x
成为新的标识符

如果要使用任意函数,只需使用参数化活动模式:

let (|Id|_|) f x = f x

match seq [] with
| Id (Seq.tryPick Some) _ -> ...
编辑

有关名称解析的详细信息,请参见规范中的。关键是有一个逻辑PatItems表,它不同于表达式中用于名称的ExprItems表。在您添加到问题编辑中的特定情况下,
X
的最后一个定义获胜,因此在这种情况下它被视为活动模式(当模式中出现
X
时,有效地隐藏文字)


除了名称冲突/阴影问题之外,我怀疑在模式中允许更大范围的表达式也会导致不明确的解析,尽管我不能马上想到任何解析。

回答这个问题的一个方法是说活动模式(作为一个特殊的语法类别)使使用相同的名称在表达式中构造值和在模式中解构值成为可能

例如,假设我们使用类型
Info
来表示一些信息:

type Info = I of string * int
您可以编写两个函数来构造和销毁此类型的值:

val constructInfo : string * int -> Info
val destructInfo  : Info -> string * int
这两个函数在本例中实现起来很简单,但有趣的是它们的类型签名是双重的。构造获取值并创建我们的(抽象)类型,销毁获取该类型并返回单个值

使用活动模式,我们可以为这两个目的使用相同的名称,
Info
。这使得它与语言的其余部分保持一致,例如
x::xs
(a,b)
都是构造函数和模式。F#库对F#quotes做了类似的事情(即,
Lambda
是类型
Expr
的模式和构造函数

因此,我们可以定义函数和模式,而不是编写构造和析构函数:

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

结果是相同的语法,
Info(a,b)
,既可以作为模式出现,也可以作为表达式出现。

回答这个问题的一种方法是,活动模式(作为一种特殊的语法类别)可以使用相同的名称来构造表达式中的值并在模式中解构它

例如,假设我们使用类型
Info
来表示一些信息:

type Info = I of string * int
您可以编写两个函数来构造和销毁此类型的值:

val constructInfo : string * int -> Info
val destructInfo  : Info -> string * int
这两个函数在本例中实现起来很简单,但有趣的是它们的类型签名是双重的。构造接受值并创建我们的(抽象)类型,而销毁接受类型并返回单个值

使用活动模式,我们可以使用相同的名称,
Info
,用于这两个目的。这使得它与语言的其他部分保持一致-例如
x::xs
(a,b)
都是构造函数和模式。F#library对F#引号做类似的事情(即,
Lambda
是类型
Expr
的模式和构造函数

因此,我们可以定义函数和模式,而不是编写构造和析构函数:

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

结果是相同的语法,
Info(a,b)
,既可以作为模式也可以作为表达式出现。

好主意。我没有想到有一个
Id
活动模式。但是我不确定我是否得到了名称空间的解释。请参阅我问题的更新。你能解释一下模式中使用的名称是如何解析的吗?Re:你的最后一句话——我发现了这一点,并更新了我的qu