在OCaml中什么时候需要驳回案件?

时间:2018-10-13 16:52:28

标签: pattern-matching ocaml gadt

在OCaml官方文档的GADTs section of the "Language extensions" chapter中,引入了int[] lastItems = myList.stream().mapToInt(x -> x.get(x.size() - 1)).toArray(); int maxValue = Arrays.stream(lastItems).max().getAsInt(); for (int i = 0; i < lastItems.length; i++) { if (maxValue == lastItems[i]) System.out.println("MAx value = " + lastItems[i] + " with index " + i); } 形式的反驳案例。但是,我认为模式匹配已经十分详尽,因此我不确定何时需要反驳。

文档中给出的示例如下:

_ -> .

但是,即使是文档人员也指出,这种驳斥案例是多余的。是否有一个示例,需要对代码类型进行反驳?

1 个答案:

答案 0 :(得分:14)

引用案例对于穷举检查很有用,而不是直接进行类型检查。

您的示例有点令人困惑,因为当模式匹配足够简单时,编译器会自动添加简单的反驳案例| _ -> .。换句话说,

let deep : (char t * int) option -> char = function None -> 'c'

等同于

let deep : (char t * int) option -> char = function
  | None -> 'c'
  | _ -> .

因为类型检查器本身添加了一个反驳案例。在4.03中引入驳斥案例之前,写deep的唯一方法是

let deep : (char t * int) option -> char = function
  | None -> 'c'
  

警告8:此模式匹配并不详尽。这是一个不匹配的值的示例:
      一些_

在那个时间点上,没有办法消除此警告(不禁用它),因为其余的情况在语法上是可能的,但是受到某些类型约束的禁止。

此处提供了引用案例以解决此问题,在这些简单案例中会自动添加它们。但是在更复杂的情况下,手写反驳案是必要的。例如,如果我从此功能开始

let either : (float t, char t) result -> char = ...

无法使用具有正确类型的具体模式来完成省略号...

let either : (float t, char t) result -> char = function
  | Ok Int -> ... (* no, wrong type: (int t, _ ) result *)
  | Ok Bool -> ... (* still no possible (bool t, _) result *)
  | Error Int -> ... (* not working either: (_, int t) result *)
  | Either Bool -> ... (* yep, impossible (_, bool t) result *)

引用案例是一种向类型检查器指示模式的其余案例与现有类型约束不兼容的方法

let either : (float t, char t) result -> char = function
  | Ok _ -> .
  | _ -> .

更准确地说,这些反驳案例告诉编译器尝试扩展案例左侧的所有_模式,并检查是否无法对这些模式进行类型检查。

通常,在三种情况下需要手写驳斥案:

  • 没有任何可能值的类型上的模式匹配
  • 未添加自动驳斥案例
  • 默认的反例探索深度不够

首先,最简单的玩具示例发生在没有可能的模式时:

let f : float t -> _ = function _ -> . 

第二种情况是当一种情况超出默认反驳情况时。特别是,仅当match中有一个案例时才添加驳斥案例:

type 'a ternary = A | B | C of 'a
let ternary : float t ternary -> _ = function
  | A -> ()
  | B -> ()
  

警告8:此模式匹配并不详尽。   这是不匹配的情况的示例:   C _

因此需要手写的案子

let ternary : float t ternary -> _ = function
  | A -> ()
  | B -> ()
  | _ -> .

最后,有时反例的默认探索深度不足以证明没有反例。 默认情况下,探索深度为1:模式_爆炸一次。 例如,在您的示例中,将| _ -> .转换为Int | Bool -> .,然后类型检查器检查是否没有大小写有效。

因此,使驳斥情况成为必需的一种简单方法是嵌套两个类型构造函数。例如:

let either : (float t, char t) result -> char = function
  | _ -> .
  

错误:无法反驳此匹配项。          这是可以达到的值的示例:_

这里,有必要手动扩展OkError情况中的至少一种:

let either : (float t, char t) result -> char = function
  | Ok _ -> . 
  | _ -> .

请注意,对于只有一个构造函数的类型有一种特殊情况,展开时仅占完整扩展的1/5。例如,如果您引入类型

type 'a delay = A of 'a

然后

let nested : float t delay option -> _ = function
  | None -> ()

很好,因为将_扩展到A _的费用为0.2,而且我们还有一些预算将A _扩展到A Int | A Float

尽管如此,如果您嵌套了delay个,则会显示警告

let nested : float t delay delay delay delay delay delay option -> _ = function
  | None -> ()
  

警告8:此模式匹配并不详尽。   这是不匹配的情况的示例:   一些(A(A(A(A(A _))))))

可以通过添加驳斥案例来修复警告:

let nested : float t delay delay delay delay delay delay option -> _ = function
  | None -> ()
  | Some A _ -> .