Проблема в том, что после того, как вы ограничили подпись M
до S
, о M.t
осталось недостаточно информации для реализации значимой функции to_string
.
Действительно, модуль типа S
определяет черный ящик (абстрактный тип) t
, который может быть получен из целого числа ... и все. Другими словами, вы можете создавать только значения типа M.t
, но никогда не иметь никакого представления о содержании этих значений. Таким образом, единственная опция, оставленная для to_string
, - игнорировать ее аргумент и возвращать несвязанную строку. Например,
module ME: SE = struct
include M
let to_string _ = "?"
end
Определение M
и ME
в обратном порядке работает лучше. Сначала мы определим модуль с более исчерпывающим API:
module ME : SE = struct
type t = unit
let of_int _ = ()
let to_string () = "()"
end
тогда мы можем стереть функцию to_string
с ограничением подписи
module M: S = ME
Другой вариант - избегать абстрактного типа t
, как вы обнаружили, используя либо точный тип модуля S with type t = int
, либо позволяя компилятору выводить его.
Короче говоря, ограничения подписи касаются стирания информации: они позволяют скрыть некоторую информацию о реализации. А сокрытие слишком большого количества информации может привести к бесполезным модулям.