简体   繁体   English

为什么不能在F#中对非局部活动模式进行参数化?

[英]Why can't non-partial active patterns be parameterized in F#?

The following F# code works as I expected, printing `Matched as 'A': 下面的F#代码按我的预期工作,打印`匹配为'A':

let (|Char|_|) convf = function
    | LazyList.Nil -> None
    | LazyList.Cons (x, _) -> Some (convf x)

let test = function
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x
    | _ -> printfn "Didn't match"

test (LazyList.of_list ['a'])

However, if I change Char from a partial active pattern to a complete active pattern as follows: 但是,如果我将Char从部分活动模式更改为完全活动模式,如下所示:

let (|Char|NoChar|) convf = function
    | LazyList.Nil -> NoChar
    | LazyList.Cons (x, _) -> Char x

let test = function
    | Char System.Char.ToUpper x -> printfn "Matched as %A" x
    | NoChar System.Char.ToUpper -> printfn "Didn't match"

test (LazyList.of_list ['a'])

Then the code fails to compile, giving the following error message: error FS0191: Only active patterns returning exactly one result may accept arguments. 然后,代码无法编译,并显示以下错误消息: error FS0191: Only active patterns returning exactly one result may accept arguments.

This example may look somewhat contrived, but it's a simplified version of an active pattern I tried to use in a Prolog lexer I've been working on in my spare time. 该示例可能看起来有些虚构,但这是我在业余时间一直在研究的Prolog词法分析器中尝试使用的主动模式的简化版本。 I can easily rewrite my code to avoid this problem, but I'm curious about why this sort of code is disallowed. 我可以轻松地重写代码来避免此问题,但是我很好奇为什么不允许使用这种代码。

Update: the newer versions of F# seem to have renamed this error: 更新: F#的较新版本似乎已重命名此错误:

error FS0722: Only active patterns returning exactly one result may accept arguments

NB. 注意 This is exactly what Brian said, but hopefully stated in a clearer fashion. 这正是Brian所说的,但希望以更清晰的方式表述。

I recall logging a bug on precisely this issue and IIRC this is what Don Syme had to say on the matter. 我记得恰好在此问题上记录了一个错误,而IIRC正是Don Syme在此问题上所说的。

A multi-case active pattern is a conversion function from some input value into one of several output values. 多案例活动模式是从某个输入值到多个输出值之一的转换函数。 In your example, any character is converted to the Char case or the NoChar case. 在您的示例中,任何字符都将转换为Char大小写或NoChar大小写。

The benefit of this is that the F# compiler calls the multi-case active pattern function once and can then generally determine which pattern match rule to evaluate next. 这样做的好处是F#编译器一次调用多大小写活动模式函数,然后通常可以确定接下来要评估的模式匹配规则。

If you allow a parameter however, then you need to evaluate the multi-case active pattern for every pattern match rule. 但是,如果允许使用参数,则需要为每个模式匹配规则评估多案例活动模式。

So imagine the following 所以想像以下

match input with
| Alpha "foo" -> ...
| Bravo "bar" -> ...

When evaluating (|Alpha|Bravo|) "foo" returned 'Bravo', then the first rule wouldn't match. 评估(| Alpha | Bravo |)“ foo”返回“ Bravo”时,第一个规则将不匹配。 Likeways (|Alpha|Bravo|) "bar" returns 'Alpha', then the second rule wouldn't match either. 同样(| Alpha | Bravo |)“ bar”返回'Alpha',则第二个规则也不匹配。 So you don't really have a multi-case active pattern. 因此,您实际上并没有多案例活动模式。 Just a paramterized, partial active pattern. 只是参数化的部分活动模式。 (Because for some inputs the expected pattern-case won't be hit.) (由于某些输入,不会遇到预期的模式情况。)

So when confronted with a corner of the language that doesn't make a whole lot of sense, and in fact can be made much clearer by the partial, parameterized active pattern. 因此,当遇到语言的一角并没有多大意义时,实际上可以通过部分参数化的活动模式使它更加清晰。 The feature wasn't added to the language. 该功能未添加到语言中。

I can't say for certain (don't know actual design rationale), but trying to reverse-engineer it, what would you expect this code to do? 我不能肯定地说(不知道实际的设计原理),但是尝试对其进行反向工程时,您希望这段代码做什么?

let (|Char|NoChar|) pred = function    
    | LazyList.Nil -> NoChar    
    | LazyList.Cons (x, _) -> if pred x then Char x else NoChar
let test = function    
    | Char System.Char.IsLetter x -> printfn "Matched as %A" x    
    | NoChar System.Char.IsDigit -> printfn "Didn't match"
test (LazyList.of_list ['a'])
test (LazyList.of_list ['1'])

Given that non-partial active patterns are supposed to partition the whole space, it would be weird if you gave each a different argument inside the same match, because then they might 'both fail' or 'both succeed'. 考虑到非局部活动模式应该划分整个空间,如果在同一个匹配项中给每个参数一个不同的参数,那将很奇怪,因为那样的话,它们可能“失败”或“都成功”。 (It's also suggestive of how they may implemented, eg just as patterns that capture their argument before doing the match. The captured argument would be invariant across all branches of the match.) (这也暗示了它们的实现方式,例如,就像在进行比赛之前捕获其自变量的模式一样。捕获的自变量在比赛的所有分支之间都是不变的。)

It also suggests that you could write eg 这也表明你可以写例如

let test convf l = 
    let (|Char|NoChar|) = function    
        | LazyList.Nil -> NoChar    
        | LazyList.Cons (x, _) -> Char(convf x)
    match l with
    | Char x -> printfn "Matched as %A" x    
    | NoChar -> printfn "Didn't match"
test System.Char.ToUpper (LazyList.of_list ['a'])

(though I dunno if this is convenient/realistic for your particular app). (尽管我不知道这对您的特定应用程序是否方便/现实)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM