在OCaml中使用相互递归的模块定义时,即使在.ml
文件中也必须提供签名。这是一个烦恼,我也希望从.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
这是我正在做的事情的粗略近似(Client
类型对象知道实例化它们的Server
。Server
知道他们的Client
s。 / p>
当然,签名会在.mli
中重复出现。为什么这有必要?
(注意:我不是在抱怨,但实际上想知道是否存在类型理论或“硬编译器问题”相关的原因。)
答案 0 :(得分:7)
据我所知,没有办法解决这个问题。在很高的层次上,就编译器而言,Client的类型签名是完整的,直到它知道Server的类型签名,反之亦然。原则上,有一种解决方法:编译器可以在编译时交叉引用.mli文件。但这种方法有缺点:它混合了编译器和链接器的一些职责,并使模块化编译(没有双关语意)更难。
如果您有兴趣,我推荐Xavier Leroy的original proposal用于递归模块。
答案 1 :(得分:4)
我的猜测:为了编译递归模块,编译器需要输入类型注释才能实现。在mli文件中(如果你使用的话)可以进一步限制或隐藏这些模块的类型,因此在一般情况下,编译器期望在mli wrt解析类型递归中找到有用的类型是不明智的。