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

时间:2011-11-14 18:34:07

标签: 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

我知道活动模式可以被称为函数,因此可以通过定义模式来简化前面的示例。但是,放弃特殊模式语法简化了这一点。

特殊语法的原因是什么?

修改

在下文中,活动模式会影响文字。

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

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

为什么模式中的函数调用也不起作用?

编辑2

我发现了一个含糊不清的场景

let zero n = if n = 0 then Some() else None
match 0 with
| zero -> //function call or capture?

在我看来,这澄清了为什么活动模式必须以大写字母开头 - 它使意图更清晰并且使阴影(例如在我之前的示例中)更不可能。

2 个答案:

答案 0 :(得分:5)

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

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

type Info = I of string * int

您可以编写两个函数来构造和销毁此类型的值:

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

在这种情况下,这两个函数很容易实现,但有趣的是它们的类型签名是双重的。构造获取值并创建我们的(抽象)类型,销毁获取类型并返回单个值。

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

因此,我们可以定义一个函数和一个模式,而不是编写 construct destruct 函数:

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

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

答案 1 :(得分:1)

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

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

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

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

修改

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

除了名称冲突/阴影问题之外,我怀疑还有一些方法允许模式中更广泛的表达式会导致模糊的解析,尽管我无法想到任何副作用。