F#模式匹配奇点

F#模式匹配奇点,f#,pattern-matching,F#,Pattern Matching,考虑以下无意义的lambda: function | [] -> "Empty list" | hd::tl -> "Not so empty list" 这个很好用。现在我将其改写如下: function | [] -> "Empty list" | hd::tl & l -> "Not so empty list" 同样,出于无意义的原因(我知道我可以通过使用as而不是&达到同样的效果,但这一切都与一个与此问题无关的代码高尔夫问题有关)。现在F#编译器告诉

考虑以下无意义的lambda:

function
| [] -> "Empty list"
| hd::tl -> "Not so empty list"
这个很好用。现在我将其改写如下:

function
| [] -> "Empty list"
| hd::tl & l -> "Not so empty list"
同样,出于无意义的原因(我知道我可以通过使用
as
而不是
&
达到同样的效果,但这一切都与一个与此问题无关的代码高尔夫问题有关)。现在F#编译器告诉我:

警告FS0025:此表达式上的模式匹配不完整。对于 例如,值“[]”可能表示本协议未涵盖的情况 图案

这没有任何意义-我在第一条规则中显式地处理
[]
的情况。关于
[]
,我看不出从第一个函数到第二个函数有什么变化;两个函数的第二条规则都不匹配,但只有第二条函数给出警告。我所做的只是添加一个与任何内容匹配的附加模式

当然,使用空列表调用第二个函数会成功

出现此警告是否有合理的原因,或者F#模式验证只是有一些怪癖?当使用更高级的模式时,我可以看到一些类似的情况,但这似乎是一个非常基本的情况。即使问题不能普遍解决,这种情况似乎足够常见,需要在编译器中进行特殊处理

我认为F#编译器在这种情况下是实用的

最后,第二条规则可以表示为对输入列表的约束
xs

xs = hd :: tl && xs = l
F#编译器似乎没有探索
&
约束。这是合理的,因为约束可以是任意复杂的,并且很少使用
&

我们在以下方面也有类似的问题:

要解决此问题,您可以:

  • 使用
    作为
    而不是
    &
    ,这更合适,因为
    l
    只是一个绑定
  • 在末尾添加通配符模式以消除警告。我通常写信

    let f =
        function
        | hd::tl & l -> "Not so empty list"
        | _ -> "Empty list"
    
  • 使用
    nowarn“25”
    抑制警告


我想你发现了另一种情况(除了
when
子句和部分活动模式),编译器的决策过程不够强大。(这就是为什么警告信息可能会显示:-)

如果您希望在没有警告的情况下获得相同的功能,那么您可以使用这样一个完整的活动模式(但实际上,对于列表,我可能只会使用@pad建议的空列表的
):


我从未见过
&
以这种方式使用。你能链接到一些关于它的功能的文档吗?@JaredPar-请看这里的和模式。基本上,这意味着你可以在一个goYeah中得到头部、尾部和整个列表。我以前见过部分活动模式的警告。不过,这一点实际上是有道理的,因为活动模式是编译函数而不是宏,因此编译器无法静态验证匹配的完整性。现在,如果有某种方法用
inline
关键字定义活动模式(我已经尝试过了),那可能是另一回事。你可以对活动模式使用
inline
让inline(| Empty | | | |)xs=将xs与[]->some()| |->None
匹配,但警告仍然存在。我想你是对的。虽然如果我做了
Some()|->None@@@>
我看到匹配表达式的AST只是一个if/else,因此模式信息仍然丢失。它确实说“maybindicate”,但它也说“complete”而不是“maybullycomplete”,因此有点含糊不清。
let f =
    function
    | hd::tl & l -> "Not so empty list"
    | _ -> "Empty list"
let (|Empty|NonEmpty|) l =
  match l with [] -> Empty | x::xs -> NonEmpty(x, xs, l)

let foo = function  
  | Empty -> 0
  | NonEmpty(x, xs, l) -> 1