模块签名丢失类型精度 [英] Losing type precision from module signature

查看:69
本文介绍了模块签名丢失类型精度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Let's say I had a simple module MyFoo that looks something like this

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

有了这个定义,它可以很好地工作并且符合预期-我可以做类似

With this definition, it works great and as expected — I can do something like

let _ = MyFoo.A "";;
- : MyFoo.t = MyFoo.A ""

没有任何问题.

现在,也许我想创建一个使用这种结构的模块的函子,所以我定义了一个模块签名,通常描述它的外观并将其命名为BaseFoo

Now maybe I want to create a functor that consumes modules with this structure, so I define a module signature that describes generally what this looks like and call it BaseFoo

module type BaseFoo = sig
  type t
  val to_string : t -> string
end

如果我以相同的方式重新定义MyFoo,但给它这样的签名

If I redefine MyFoo the same way but giving it this signature like

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的精度(是否有更好的方法来描述此处发生的情况?)—例如:

I lose the precision of its type t (is there a better way to describe what happens here?) — for example:

let _ = MyFoo.A "";;
Error: Unbound constructor MyFoo.A

这里到底发生了什么,为什么会发生?是否有解决此类问题的规范方法(除了放弃签名之外)?

What exactly is going on here and why does it happen? Is there a canonical way for dealing with this kind of problem (besides just leaving off the signature)?

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

I've tried manually including the signature and the specific type type definition too but get a different kind of error (this probably isn't the right approach).

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.

推荐答案

您不需要签名

发生的事情几乎就是您所描述的:给MyFoo签名中的MyFoo签名将其限制为签名.

You don't need the signature

What is going on is pretty much what you describe: giving MyFoo the BaseFoo signature in its definition restricts it to the signature.

为什么?因为这就是在此处指定签名的目的.规范的解决方案是不使用签名(通常,将签名定义放在模块定义旁边对于读者来说已经足够清楚了.)

Why? Because this is what specifying a signature at this place is for. The canonical solution is to leave of the signature (usually, letting the signature definition next to the module definition will be clear enough for the reader).

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

Note that when you call MyFoo on your functor, the signature will be checked. My usual choice is to rely on that.

鉴于您的尝试,我想这可能对您很有趣:

Given what you've tried, I guess this could be interesting to you:

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'子句允许您注释模块签名以向其添加类型信息.这非常有用,尤其是在处理函子时.有关更多信息,请参见此处.

The with type t = t' clause allows you to annotate a module signature to add type information to it. It is quite useful, especially when dealing with functors. See here for more information.

MyFooHidden似乎没有用,但是您可以将其视为MyFoo具有正确签名的检查.毕竟,您仍然可以使用MyFoo. MyFooWithType实际上(有点)用处不大,因为如果您更改签名以添加要导出的类型,则也需要在此处添加导出.

MyFooHidden may seem useless, but you can see it as a check that MyFoo has the right signature. You can still use MyFoo however you want after all. MyFooWithType is actually (a bit) less useful because if you change your signature to add a type you'd want exported, you'd need to add the export here too.

至于您的include尝试.好吧,不错的尝试!你快到了:

As for your include try. Well, nice try! You were almost there:

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,这样您就不会遇到任何双重定义问题.有关更多详细信息,请参见此处.

The with type t := t' is a bit different in that it doesn't perform an equality but a replacement. The type t definition is removed from the BaseFoo signature altogether and all instances are replaced with your own t, that way you don't get any double definition problem. See here for more details.

正如您指出的那样,这可能不是您想要的方法,因为您不再轻易知道MyFoo的确是BaseFoo.

As you point out, this is probably not the approach you want, as you no longer easily know that MyFoo is indeed a BaseFoo.

这篇关于模块签名丢失类型精度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆