多态变体子类型实现与签名

时间:2018-01-13 06:23:19

标签: ocaml subtyping polymorphic-variants

我有以下代码:

module Test : sig
  type +'a t
  val make : int -> [< `a | `b] t
end = struct
  type 'a t = Foo of int | Bar of string

  let make = function
    | 0 -> (Foo 0 : [`a] t)
    | _ -> (Bar "hi" : [`a] t)
end

您可能会注意到,抽象类型'a t在其类型参数'a中被声明为协变,而make构造函数被声明为返回子类型多态变体案例ab

make的实现中,返回子类型[a] t仍应遵循协方差规则,因为子类型位于返回类型位置。

但是,我收到以下错误:

Error: Signature mismatch:
       ...
       Values do not match:
         val make : int -> [ `a ] t
       is not included in
         val make : int -> [< `a | `b ] t
       File ".../cov.ml", line 3, characters 3-34:
         Expected declaration
       File ".../cov.ml", line 7, characters 7-11:
         Actual declaration

有关如何说服OCaml make函数确实返回[a | b] t的有效子类型的任何建议?

1 个答案:

答案 0 :(得分:4)

我做了一些实验:

# type 'a t = Foo of int | Bar of string;;
type 'a t = Foo of int | Bar of string
# let make = function
  | 0 -> (Foo 0 : [`a] t)
  | _ -> (Bar "hi" : [`a] t);;
val make : int -> [ `a ] t = <fun>
# (make : int -> [< `a | `b] t);;
- : int -> [ `a ] t = <fun>
# let make2 : int -> [< `a | `b] t = make;;
val make2 : int -> [ `a ] t = <fun>
# let make3 = (make :> int -> [< `a | `b] t);;
val make3 : int -> [< `a | `b ] t = <fun>

所以,显然OCaml确实认识到了超类型关系,但仍然倾向于坚持更精确的子类型,除非给予强制。其他人可能知道类型的理论原因。但正如你的问题只是

  

[...]如何说服OCaml [...]

我的回答是:像这样使用强制

module Test : sig
  type +'a t
  val make : int -> [< `a | `b] t
end = struct
  type 'a t = Foo of int | Bar of string

  let make = (function
    | 0 -> (Foo 0 : [`a] t)
    | _ -> (Bar "hi" : [`a] t)
    :> int -> [< `a | `b] t)
end