代码之家  ›  专栏  ›  技术社区  ›  Nick Zuber

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

  •  1
  • Nick Zuber  · 技术社区  · 6 年前

    假设我有一个简单的模块 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
    

    如果我重新定义 美福

    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.
    
    1 回复  |  直到 6 年前
        1
  •  3
  •   Community Paul Sweatte    4 年前

    你不需要签名

    MyFoo 这个 BaseFoo 签名在其定义中仅限于签名。

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

    注意当你打电话的时候 美福 在你的函子上,签名将被检查。我通常的选择就是依靠这个。

    一些变通办法

    鉴于你所做的尝试,我想这可能会让你感兴趣:

    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 不管你想要什么。 MyFooWithType 实际上(有点)不太有用,因为如果您更改签名以添加希望导出的类型,则也需要在此处添加导出。

    使用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 更多细节。

    美福 确实是一个 BaseFoo公司