参数模式匹配不完整

Incomplete Pattern Match on Parameters

为什么下面的代码给我一个不完整的模式匹配警告和一个运行时异常。

 type Left =
     | L1 of int
     | L2 of int

 type Right =
     | R1 of int
     | R2 of int

 type Directions =
     | LeftDir of Left
     | RightDir of Right


 let unpack (LeftDir (L1 i)) = i

 let x = unpack (LeftDir (L1 10))
 let y = unpack (LeftDir (L2 20))

显然答案是因为它是一个不完整的模式匹配 ;-)

但为什么 F# 不够聪明(或者 F# 语言规范的编写者)不知道 unpack 只需要 LeftDir containing a L1?为什么需要完整的模式匹配?

问题是无法在编译时指定 unpack 采用 Directions 的特定实例。唯一的可能性是 运行 时间检查,因此您会收到编译器警告。

您以仅处理两种可能情况之一的方式显式定义了一个函数,然后当您为未处理的第二种情况获得异常时,您会感到惊讶。

你认为否则会发生什么?

可区分联合的全部意义在于拥有一个已知数量 "subtypes" 的封闭类型,您可以用详尽的方式处理它。

您可以很好地编写一个只关心特定情况的函数,例如 LeftDir (L1 x),但该函数仍然需要能够处理其他情况。您要么自己提供该处理,要么以不完全匹配异常的形式免费获得它。

从概念上讲,LeftDirL1 不是类型(尽管它们在 IL 级以这种方式实现);相反,Directions 是一种类型,而 LeftDir 只是该类型的一个构造函数,LeftL1 也是如此。如果这不立即有意义,请重新审视代数和类型的概念。

由于 F# 函数参数是严格基于类型的(与某些其他语言不同),unpack 必须采用 Directions 类型的对象,并且该对象实例是否是通过 [=10= 构造的] 或 RightDir 必须在运行时确定;如果通过 LeftDir,同样地,它包含的 Left 实例是通过 L1 还是 L2.

构造的

完全的模式匹配不是绝对必要的,但鉴于 F# 等强静态类型语言的核心目标是在编译时捕获尽可能多的错误尽可能给出警告推荐完整的模式匹配以避免运行时错误。