Ocaml 从模块签名中丢失类型精度

Ocaml 从模块签名中丢失类型精度,ocaml,Ocaml,假设我有一个简单的模块MyFoo,看起来像这样 module MyFoo = struct type t = | A of string | B of int let to_string thing = match thing with | A str -> str | B n -> string_of_int n end 有了这个定义,它工作得很好,正如预期的那样-我可以做类似的事情 let _ = MyFoo.A "";; -

假设我有一个简单的模块
MyFoo
,看起来像这样

module MyFoo = struct
  type t =
    | A of string
    | B of int

  let to_string thing =
    match thing with
    | A str -> str
    | B n -> string_of_int n
end
有了这个定义,它工作得很好,正如预期的那样-我可以做类似的事情

let _ = MyFoo.A "";;
- : MyFoo.t = MyFoo.A ""
没有任何问题

现在,也许我想创建一个使用这种结构的模块的函子,所以我定义了一个模块签名,它通常描述了这是什么样子,并称之为
BaseFoo

module type BaseFoo = sig
  type t
  val to_string : t -> string
end
如果我以同样的方式重新定义MyFoo,但是给它这样的签名

module MyFoo : BaseFoo = struct
  type t =
    | A of string
    | B of int

  let to_string thing =
    match thing with
    | A str -> str
    | B n -> string_of_int n
end
我失去了它的类型
t
(有没有更好的方法来描述这里发生的事情?)-例如:

let _ = MyFoo.A "";;
Error: Unbound constructor MyFoo.A
这里到底发生了什么?为什么会发生?有没有一种规范的方法来处理这类问题(除了去掉签名之外)

我也尝试过手动包含签名和特定类型定义,但得到了另一种错误(这可能不是正确的方法)

你不需要签名 所发生的事情与您描述的差不多:给定
MyFoo
其定义中的
BaseFoo
签名将其限制为签名

为什么??因为这就是在这里指定签名的目的。规范的解决方案是保留签名(通常,让签名定义位于模块定义旁边对读者来说足够清晰)

请注意,当您在functor上调用
MyFoo
时,将检查签名。我通常的选择是依靠这一点

一些变通办法 考虑到你所做的尝试,我想这可能会让你感兴趣:

module type BaseFoo = sig  ... end
module MyFoo = struct ... end

module MyFooHidden : BaseFoo = MyFoo (* Same as defining MyFoo : BaseFoo *)
module MyFooWithType :
   BaseFoo with type t = MyFoo.t
   = MyFoo (* What you want *)
with type t=t'
子句允许您对模块签名进行注释,以向其添加类型信息。它非常有用,尤其是在处理函子时。有关更多信息,请参阅

MyFooHidden
可能看起来没用,但您可以将其视为检查MyFoo是否具有正确的签名。你仍然可以使用
MyFoo
不管你想要什么
MyFooWithType
实际上(有点)不太有用,因为如果您更改签名以添加希望导出的类型,您也需要在此处添加导出

使用包含 至于你的
包括
试试。好吧,很好的尝试!你就快到了:

module MyFoo : sig
 type t = A of string | B of int
 include BaseFoo with type t := t
end
类型为t:=t'
有点不同,它不执行相等,而是执行替换。类型
t
定义从
BaseFoo
签名中删除,所有实例都替换为您自己的
t
,这样您就不会出现任何双重定义问题。有关更多详细信息,请参阅

正如您所指出的,这可能不是您想要的方法,因为您不再容易知道
MyFoo
确实是
BaseFoo

module MyFoo : sig
 type t = A of string | B of int
 include BaseFoo with type t := t
end