ocaml 多态变体类型推断类型过于笼统

ocaml polymorphic variant type inferred type too general

我有这个代码

let my_fun: [`X | `Y | `Z] -> [`Z | `Y] = function
  | `X -> `Y
  | x -> x

它不会编译消息

11 |   | x -> x
          ^
Error: This expression has type [ `X | `Y | `Z ]
       but an expression was expected of type [ `Y | `Z ]
       The second variant type does not allow tag(s) `X

为什么 complire 不能推断类型 [<code>Z |Y] 如果很明显 `X 永远不会从函数返回?

如果 x 的类型被推断为 [`Y | `Z],那么它不能用于 [`X | `Y | `Z] 类型,这很奇怪,因为它绑定到一个参数那种。

您可以使用 as 模式来获得具有精炼类型的绑定,如下所示:

let my_fun: [`X | `Y | `Z] -> [`Y | `Z] = function
  | `X -> `Y
  | (`Y | `Z) as yz -> yz

如果您想对许多情况下的多态变体执行此操作,#type_name 模式会非常有用。

Why can't the compiler infer the type [Z |Y] if it is clear that `X can be never returned from the function?

一个更简单的例子是:

if false then 0 else "hello"

编译器必须拒绝它,因为在 ML 语言中键入要求两个分支具有相同的类型 1,并且类型 intstring不能统一;这是真的,即使你可以说表达式永远不可能计算为 0(请记住,从形式上讲,说我们计算一个不属于所定义语言的表达式是没有意义的)。

match表达式中,所有子句左边部分的类型必须统一为被匹配表达式的类型。所以最后一个子句中的 xfunction 中的隐式参数具有相同的类型,这是签名中声明的输入类型。无论 `X 在该上下文中是否为有效值,都是如此。

你需要列举出所有有效的情况;如果您在代码中经常需要它,那么您最好为此编写一个专用函数:

let f : ([> `Y | `Z ] -> [`Y | `Z ] option) = function
  | (`Y|`Z) as u -> (Some u) 
  | _ -> None;

例如:

# f `O;;
- : [ `Y | `Z ] option = None
# f `Y;;
- : [ `Y | `Z ] option = Some `Y

另见 Set-Theoretic Types for Polymorphic Variants (pdf)


[1] 为了比较,在动态类型的Common Lisp中,等效表达式是有效的; SBCL 编译器完成的静态分析可以推断其类型,即长度为 5 的字符串:

> (describe (lambda () (if nil 0 "hello")))
....
Derived type: (FUNCTION NIL
               (VALUES (SIMPLE-ARRAY CHARACTER (5)) &OPTIONAL))