如何在保留其签名的同时将一个模块包含在另一个模块中?

时间:2019-04-05 21:48:03

标签: types module ocaml

我正在尝试实现一个在其他模块中重用模块的小例子,但是到目前为止,我还没有完全成功。

我想要实现的是创建2个签名-S和SE(SE包括S),以及2个模块-M(实现S)和ME(实现SE),目的是不重复先前的代码。

我所缺少的是包括我内部M的内容:

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

到目前为止,通过在ME中取消注释(* include M *)并将M的定义更改为module M : S with type t = int = struct,找到了一种解决方法,但是这种做法无法实现此目的,因为它从实现S更改了M的定义实现类似S的东西。

当然,此练习必须有适当的解决方案。那我想念什么?

1 个答案:

答案 0 :(得分:2)

问题是,一旦将M的签名限制为S,就没有足够的有关M.t的信息来实现有意义的to_string功能。 / p>

实际上,类型为S的模块定义了一个黑匣子(抽象类型)t,它可以由整数产生……仅此而已。换句话说,您只能产生M.t类型的值,而对这些值的内容一无所知。因此,to_string剩下的唯一选择是忽略其参数,并返回不相关的字符串。例如,

 module ME: SE = struct
   include M
   let to_string _ = "?"
 end

以相反的顺序定义MME效果更好。首先,我们使用更为详尽的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类型变得抽象,如您所发现。

简而言之,签名约束与信息擦除有关:它们允许隐藏一些有关实现的信息。隐藏太多信息可能会导致模块失效。