4

我有这个代码

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

如果很明显 `X 永远不能从函数中返回,为什么编译器不能推断类型?[Z |Y]

4

2 回答 2

4

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

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

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

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

于 2019-11-15T04:01:13.247 回答
2

如果很明显 `X 永远不能从函数中返回,为什么编译器不能推断类型 [Z |Y]?

一个更简单的例子是:

if false then 0 else "hello"

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

在一个match表达式中,所有子句的左边部分的类型必须统一到被匹配的表达式的类型。所以x在最后一个子句中的类型与 in 中的隐式参数相同function,即签名中声明的输入类型。无论在该上下文中是否`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

另请参阅多态变体的集合论类型(pdf)


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

> (describe (lambda () (if nil 0 "hello")))
....
Derived type: (FUNCTION NIL
               (VALUES (SIMPLE-ARRAY CHARACTER (5)) &OPTIONAL))
于 2019-11-15T09:28:40.727 回答