Module 为什么在OCaml中要求在相互递归的模块中使用签名?

Module 为什么在OCaml中要求在相互递归的模块中使用签名?,module,ocaml,mutual-recursion,Module,Ocaml,Mutual Recursion,在OCaml中使用相互递归的模块定义时,必须给出签名,即使在.ml文件中也是如此。我还想从.mli中公开一个给定的接口,因为我最后重复了两次签名,这是一个麻烦( 这是我所做工作的粗略近似(Clienttype对象知道实例化它们的Server。Servers知道它们的Clients) 当然,签名会在.mli中重复。为什么需要这样做 (注意:我不是在抱怨,而是想知道是否存在类型理论或“硬编译器问题”——与此相关的原因。)据我所知,没有办法解决这个问题。在一个非常高的层次上,就编译器而言,客户端的类型

在OCaml中使用相互递归的模块定义时,必须给出签名,即使在
.ml
文件中也是如此。我还想从
.mli
中公开一个给定的接口,因为我最后重复了两次签名,这是一个麻烦(

这是我所做工作的粗略近似(
Client
type对象知道实例化它们的
Server
Server
s知道它们的
Client
s)

当然,签名会在
.mli
中重复。为什么需要这样做


(注意:我不是在抱怨,而是想知道是否存在类型理论或“硬编译器问题”——与此相关的原因。)

据我所知,没有办法解决这个问题。在一个非常高的层次上,就编译器而言,客户端的类型签名在它知道服务器的类型签名之前是不完整的,反之亦然。原则上,有一种方法可以解决这个问题:编译器可以在编译时交叉引用你的.mli文件。但这种方法缺点:它混合了编译器和链接器的一些职责,使得模块化编译(没有双关语)更加困难


如果您感兴趣,我推荐Xavier Leroy用于递归模块。

我的猜测:为了编译递归模块,编译器需要类型注释来实现。在mli文件中(如果您使用任何)这些模块的类型可以进一步限制或完全隐藏,因此在一般情况下,编译器期望在mli wrt解析类型递归中找到有用的类型是不明智的。

这是有道理的。事实上,我利用了这个“特性”通过在
.mli
中为外部使用者公开不同的类型签名,我应该意识到。猜得好。类型注释是必需的,因为在相互递归的模块和函子的情况下,推理通常是不可判定的。文献中充满了细化类型系统以优化最小注释需求的尝试uirements并保持其可靠性。是否可以改进OCaml的类型系统以降低注释需求负担是有争议的。感谢链接!类型理论略高于我,但仍然是一本很好的书。但是我想正如ygrek提到的,不是由.mli声明编译实际模块本身时使用的类型。它会如你所说,如果它试图这样做,它会变得更丑陋。
module rec Client : sig
  type ('serv,'cli) t

  (* functions ... *)
end = struct
  type ('serv,'cli) t =
    { server: ('serv,'cli) Server.t
    ; (* other members ... *)
    }
end
and Server : sig
  type ('serv,'cli) t

  (* functions ... *)
end = struct
  type ('serv,'cli) t =
    { mutable clients: ('serv,'cli) Client.t list
    ; mutable state: 'serv
    }

  (* functions again ... *)
end