2011-01-20 57 views
9

在OCaml中使用相互递归模块定义时,即使在.ml文件中也需要签名。这是一个烦恼,我也想暴露.mli给定的接口,因为我最终重复签名两次。 :(!为什么要在OCaml中的相互递归模块中签名?

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类型的对象知道实例化他们ServerServer■正确的Client S)。

当然,签名。反复在.mli为什么这是必要的

(注:我不是在抱怨,但实际上想知道是否有一种理论或“硬编译器问题”有关的原因)?

回答

4

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

+0

这很有道理。事实上,我通过在`.mli`中暴露外部消费者的不同类型签名来利用这个“特征”。我应该已经意识到了。 – Ashe 2011-01-21 11:02:39

7

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

如果您有兴趣,我推荐Xavier Leroy的original proposal递归模块。

+0

感谢您的链接!类型理论略高于我,但仍然是一个很好的阅读。但是我想像ygrek提到的那样,不是为了.mli声明用于编译实际模块本身的类型。正如你所提到的,如果它试图这样做,它会变得更加难看。 – Ashe 2011-01-21 11:08:52