types - 通配符模式覆盖多态变体的子类型约束

标签 types ocaml type-inference subtyping polymorphic-variants

鉴于这些类型

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



问题:
  • 为什么它不再能够接受子类型?我理解通配符意味着它现在可以接受父类(super class)型,但这不应该意味着它必须。
  • 有没有办法解决这个问题,以避免枚举一百万左右不相关的变体?
  • 最佳答案

    根本的问题是为什么

    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
    

    非常明确地表明 restrictpp适用于不同的多态变体集。

    关于types - 通配符模式覆盖多态变体的子类型约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47212094/

    相关文章:

    javascript - 哪种风格更适合 checkin JavaScript?

    compiler-errors - OCaml 警告 31、编译器库和 ppx

    java - 动态类型方法中的奇数类型推断

    typescript - 映射 typescript 类型

    python - 在 Python pandas 中有选择地将 float 转换为整数和小数

    haskell - 使用 GHC 泛型时参数类型错误

    Swift 中的向量 SIMD 类型

    module - 模块 <name> = struct .. end 和模块类型 <name> = struct.. end 之间的区别?

    ocaml - ocamldoc 可以引用类型构造函数吗?

    c++ - C++11 类型推断期间的优先级规则是什么?