Полиморфный вариант реализации подтипа не соответствует сигнатуре
У меня есть следующий код:
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