模式匹配中的行为不一致

时间:2011-12-02 20:15:18

标签: f#

type Bar = A | B
type Foo = C of Bar | D of Bar
let case = Unchecked.defaultof<Foo>;;

match case with
| C A -> ""
| C B -> ""
| _ -> "Matches";;

match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"

快速浏览F#语言规范,没有关于空测试(我无论如何也不能做)似乎是相关的,两种类型似乎都是引用类型(AFAIK)。

我认为第一种情况下的行为是正确的。

5 个答案:

答案 0 :(得分:3)

我认为一旦你使用Unchecked.defaultof<_>(因此“未经检查”;-)),所有赌注都会被取消。从F#透视图中,Null不被视为类型Foo的有效值(尽管它来自.NET的角度),因此我不认为模式匹配语义是定义的。

你想做什么?

答案 1 :(得分:3)

所以阅读规范,第一个提示就在这里

  

b)将null作为异常值的类型。这些类型都可以   不承认null文字,但确实将null作为异常值。

     

此类别中的类型包括:

     

o所有F#列表,记录,元组,函数,类和接口类型。

     

o除了具有null作为正常值的那些之外,所有F#union类型   (如下一段所述)。

     

对于这些类型,使用null文字不是直接的   允许的。然而,严格地说,它可以生成一个   使用某些函数的这些类型的null值   Unchecked.defaultof。对于这些类型,null被认为是   异常值。关于空值的操作行为   在§6.9中定义。

这似乎表明,在传入一个null值时你的union可能是某种未定义的行为,因为值是“异常”,第6.9节并不是特别有帮助

查看_的定义,看来你是对的,这是一个错误 - 它说明了

  

7.1.7通配符模式

     

模式_是通配符模式并匹配任何输入。对于   例如:

     

让分类x =

match x with

| 1 -> 0

| 0 -> 1

| _ -> 0

我认为最相关的提示稍后会列出DU的编译方法

  

8.5.3从其他CLI语言使用的联合类型的编译表格

     

编译的联合类型U将具有:

     

·每个nullary联合的一个CLI静态getter属性U.C.   case C.这将得到一个代表该案例的单例对象。

     

·每个非nullary union case C的一个CLI嵌套类型U.C   此类型将具有实例属性Item1,Item2 ....   union case的字段,或单个实例属性Item,如果有的话   只是一个领域。只有一个案例的编译联合类型不会   有一个嵌套类型。相反,联合类型本身扮演着角色   案件类型。

     

·每个非Nullary union案例的一个CLI静态方法U.NewC   C.这将为该案件构建一个对象。

     

·每个返回的案例C的一个CLI实例属性u.IsC   对于案件是真是假。

     

·每个获取的案例C的一个CLI实例属性u.Tag   或者计算与案例相对应的整数标记。

由此可以看出,所有检查方法都是实例方法,这些方法需要非空值。正弦null是“异常”,生成的代码不会检查,所以它会抛出。

我认为根据_的定义,你可能会认为这实际上是一个错误。但是,修复它需要在每次DU模式匹配检查之前插入空检查,这会显着降低代码速度,所以我怀疑这是否会被修复

答案 2 :(得分:2)

Unchecked.defaultof方法的描述以句子“此函数在某些F#值没有正确的空值的意义上是不安全的”结束,这就是这里的情况。

试试这个:

let isNull = (case = null) ;;

  let isNull = (case = null) ;;
  ---------------------^^^^

stdin(3,22): error FS0043: The type 'Foo' does not have 'null' as a proper value
> 

DU意图是不可变的,没有适当的空值。

答案 3 :(得分:2)

针对具有两种情况的DU的模式匹配编译为具有类型测试的if/else。如果你翻译你的例子,行为是显而易见的。

match case with
| C A -> ""
| C B -> ""
| _ -> "Matches"

转换为

if (case is C) 
  ...
else
  "Matches"

match case with
| C A -> ""
| D B -> ""
| _ -> "Throws"

转换为

if (case is D)
  ...
else //must be C
  //check tag  BANG! NRE

和kvb的例子:match case with | C _ -> "C" | D _ -> "D"

if (case is D)
  //...
else //must be C
  "C"

我认为您可以将此视为合理的优化(与if (case is D) {...} else if (case is C) {...} else { MatchFailureException }相比),因为null行为未定义。

将第三个案例添加到Foo,问题就会消失。

答案 4 :(得分:0)

我目前无法确认这一点,但类型的AddNullLiteral属性是否允许匹配成功?