Как включить модуль в другой модуль, сохраняя при этом его подписи? - PullRequest
1 голос
/ 06 апреля 2019

Я пытаюсь реализовать небольшой пример повторного использования модулей внутри других модулей, но пока мне не удалось полностью.

Чего я хотел бы добиться, так это создать 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.

Конечно, должно быть правильное решение для этого упражнения. Так чего мне не хватает?

1 Ответ

2 голосов
/ 06 апреля 2019

Проблема в том, что после того, как вы ограничили подпись 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, либо позволяя компилятору выводить его.

Короче говоря, ограничения подписи касаются стирания информации: они позволяют скрыть некоторую информацию о реализации. А сокрытие слишком большого количества информации может привести к бесполезным модулям.

...