1

如果可以将普通函数用作模式,则不必编写琐碎的活动模式,例如

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?

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

4

2 回答 2

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,它既是模式又是 type 的构造函数Expr

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

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

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

于 2011-11-14T22:30:49.817 回答
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) _ -> ...

编辑

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

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

于 2011-11-14T18:55:03.763 回答