Я пытаюсь реализовать небольшой пример повторного использования модулей внутри других модулей, но пока мне это не удалось.
Чего я хотел бы добиться, так это создать 2 подписи - S и SE (с SE, включая S) и 2 модуля - M (реализующий S) и ME (реализующий SE) с целью не повторять предыдущий код.
Бит, который мне не хватает, включает содержимое M внутри ME:
module type S = sig
type t
val of_int : int -> t
end
module type SE = sig
include S
val to_string : t -> string
end
module M : S = struct
type t = int
let of_int x = x
end
module ME : SE = struct
(* include M *)
let to_string = string_of_int
end
Пока нашел обходной путь, раскомментировав (* include M *)
в ME и изменив определение M на module M : S with type t = int = struct
, но это противоречит цели этого упражнения, поскольку оно меняет определение M с реализации S на реализацию чего-то похожего на S.
Конечно, должно быть правильное решение для этого упражнения. Итак, что мне не хватает?
Проблема в том, что после того, как вы ограничили подпись 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
, либо позволяя компилятору сделать его вывод.
Короче говоря, ограничения подписи касаются стирания информации: они позволяют скрыть некоторую информацию о реализации. А сокрытие слишком большого количества информации может привести к бесполезным модулям.