Полиморфный вариант реализации подтипа не соответствует сигнатуре

У меня есть следующий код:

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 конструктор объявлен как возвращающий подтип вариантов полиморфного варианта a или же b,

В моей реализации 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 ответ

Решение

Я сделал несколько экспериментов:

# 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
Другие вопросы по тегам