鉴于这些类型
type a = [ `A ]
type b = [ a | `B | `C ]
和这个功能
let pp: [< b] -> string =
function | `A -> "A"
| `B -> "B"
| `C -> "C"
应用
a
类型的值按预期工作没有问题:let a: a = `A
let _ = pp a
但是,如果修改函数以包含通配符模式
let pp: [< b] -> string =
function | `A -> "A"
| `B -> "B"
| _ -> "?"
即使其他一切都保持不变,它现在会产生以下错误(在
let _ = pp a
上):This expression has type b -> string but an expression was expected of type a -> 'a Type b = [ `A | `B ] is not compatible with type a = [ `A ] The second variant type does not allow tag(s) `B
问题:
最佳答案
根本的问题是为什么
let pp= function
| `A -> "A"
| `B -> "B"
| _ -> "?"
推断为
[> `A| `B] -> string
而不是 [< `A| `B | ... ] -> string
(其中 ...
代表任何构造函数)。答案是这是一个设计选择,也是一个假阳性和假阴性之间的折衷问题:https://www.math.nagoya-u.ac.jp/~garrigue/papers/matching.pdf .更准确地说,第二种类型被认为太弱了,因为它太容易丢失
`A
的信息。和 `B
出现在 pp
.例如,考虑以下代码,其中 `b
是拼写错误,应该是 `B
:let restrict (`A | `b) = ()
let dual x = restrict x, pp x
目前,此代码失败
Error: This expression has type [< `A | `b] but an expression was expected of type [> `A | `B ]
The first variant type does not allow tag(s) `B
此时,如果
`b
是一个拼写错误,在这里可以发现错误。如 pp
已输入 [< `A|`B |..]
,双重类型将被限制为 [`A] -> unit * string
默默地,没有机会捕获这个错误。此外,以当前的类型,如果 `b
不是拼写错误,完全有可能使 dual
通过添加一些强制转换有效let dual x = restrict x, pp (x:[`A]:>[>`A]);;
(* or *)
let dual x = restrict x, (pp:>[`A] -> _) x
非常明确地表明
restrict
和 pp
适用于不同的多态变体集。
关于types - 通配符模式覆盖多态变体的子类型约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47212094/