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

  • 本文关键字:类型 精度 模块 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.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

这里到底发生了什么,为什么会发生这种情况?有没有处理此类问题的规范方法(除了省略签名(?

我也尝试手动包括签名和特定类型类型定义,但遇到了不同类型的错误(这可能不是正确的方法(。

module MyFoo : sig
include BaseFoo
type t = | A of string | B of int
end = 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 "test";;
Error: Multiple definition of the type name t.
Names must be unique in a given structure or signature.

你不需要签名

正在发生的事情几乎就是你所描述的:在其定义中给MyFooBaseFoo签名会将其限制为签名。

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

请注意,当您在函子上调用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是否具有正确的签名。毕竟,您仍然可以随心所欲地使用MyFooMyFooWithType实际上(有点(不太有用,因为如果您更改签名以添加要导出的类型,则也需要在此处添加导出。

使用包含

至于你的include尝试。嗯,不错的尝试!你快到了:

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

with type t := t'有点不同,因为它不执行相等而是替换。类型t定义从BaseFoo签名中完全删除,所有实例都替换为您自己的t,这样您就不会遇到任何双重定义问题。有关更多详细信息,请参阅此处。

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

最新更新