OCaml为function `A -> 1 | _ -> 0
提供[> `A] -> int
类型,但为什么不是[> ] -> int
?
这是我的理由:
function `B -> 0
的类型为[<`B] -> int
。添加`A -> 0
分支以使其function `A -> 1 | `B -> 0
放宽到[<`A|`B] -> int
。该函数在它可以接受的参数类型中变得更加宽松。这很有道理。function _ -> 0
的类型为'a -> int
。此类型与[> ] -> int
一致,[> ]
是已打开的类型(非常宽松)。添加`A -> 0
分支以使其function `A -> 1 | _ -> 0
将类型限制为[>`A] -> int
。这对我来说没有意义。实际上,添加另一个分支`C -> 1
将使其[>`A|`C] -> int
,进一步限制类型。为什么?注意:我不是在寻找解决方法,我只是想知道这种行为背后的逻辑。
在相关的说明中,function `A -> `A | x -> x
的类型为([>`A] as 'a) -> 'a
,虽然这也是参数的限制性开放类型,但我可以理解其中的原因。该类型应与'a -> 'a
,[>` ] -> 'b
,'c -> [>`A]
统一;唯一的方法似乎是([>`A] as 'a) -> 'a
。
我的第一个例子是否存在类似的原因?
答案 0 :(得分:6)
可能的答案是[> ] -> int
类型允许参数(`A 3)
,但function `A -> 1 | _ -> 0
不允许这样做。换句话说,类型需要记录`A
不带参数的事实。
答案 1 :(得分:6)
原因是非常实用的:
在旧版本的OCaml中,推断类型是
[`A | .. ] -> int
这意味着A不参与,但可能缺席。
但是这种类型与
是一致的[`B |`C ] -> int
导致`A在没有任何检查的情况下被丢弃。
通过拼写错误很容易引入错误。
因此,变体构造函数必须出现在上限或下限中。
答案 2 :(得分:3)
杰弗里解释说,(function `A -> 1 | _ -> 0)
的输入是合理的。
(function `A -> 1 | _ -> 0) ((fun x -> (match x with `B -> ()); x) `B)
未能进行类型检查,在我看来,应该通过表达式的后半部分来解释。实际上,函数(fun x -> (match x with `B -> ()); x)
具有输入类型[< `B]
,而其参数`B
具有类型[> `B ]
。两种类型的统一给出了闭合类型[ `B ]
,它是不多态。它无法与您从[> `A ]
获得的输入类型(function `A -> 1 | _ -> 0)
统一。
幸运的是,多态变体不仅依赖于(行)多态。您也可以在这种情况下使用子类型,例如,您要放大封闭类型的情况:[ `B ]
是一个子类型,例如,[`A | `B]
这是一个[> `A ]
的实例。在OCaml中,使用语法(expr :> ty)
(转换为某种类型),或者(expr : ty :> ty)
,如果无法推断出域类型,则显式
你可以写:
let b = (fun x -> (match x with `B -> ()); x) `B in
(function `A -> 1 | _ -> 0) (b :> [ `A | `B ])
这是很好的类型。