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

问题描述 投票:1回答:1

假设我有一个简单的模块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.
ocaml
1个回答
3
投票

You don't need the signature

发生的事情几乎就是你描述的内容:给MyFoo定义中的BaseFoo签名将其限制为签名。

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

请注意,当您在仿函数上调用MyFoo时,将检查签名。我通常的选择是依靠它。

A few workarounds

考虑到你的尝试,我想这对你来说很有意思:

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'子句允许您注释模块签名以向其添加类型信息。它非常有用,特别是在处理仿函数时。有关更多信息,请参阅here

MyFooHidden可能看起来毫无用处,但你可以看到它是MyFoo拥有正确签名的一个检查。毕竟你仍然可以使用MyFooMyFooWithType实际上(有点)不太有用,因为如果你改变你的签名来添加你想要导出的类型,你也需要在这里添加导出。

Using include

至于你的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,这样您就不会遇到任何双重定义问题。有关详细信息,请参阅here

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

© www.soinside.com 2019 - 2024. All rights reserved.