【发布时间】:2017-02-19 19:24:05
【问题描述】:
我已经阅读了一些类似的问题和一些在线示例,但我仍然不知道在这种特殊情况下如何编写.mli 文件。
在我的项目中,我使用了一个带有 int 键的通用映射模块,然后我根据我想要存储的值进行专门化。假设我想用它来存储对,所以我有一个这样的文件:
file dataStr.ml:
module IntOrder =
struct
type t = int
let compare = Pervasives.compare
end
module IntMap = Map.Make( IntOrder )
type couple = int * int
(* pretty names *)
type int2couple = couple IntMap.t
module Couples = struct type t = int2couple end
使用这个子模块的文件是:
file useMap.ml:
open DataStr
let use k m =
IntMap.add k ((Random.int 6), (Random.int 8)) m
带接口:
file useMap.mli:
open DataStr
val use : int -> Couples.t -> Couples.t
到目前为止,一切都很好。
现在假设我想公开子模块Couples,但不输入int2couple。然后我会写这个接口:
file dataStr.mli:
module IntMap : Map.S with type key = int
module Couples : sig type t end
(*
I'd like to avoid the redundancy of using
module Couples : sig type t = (int * int) IntMap.t end
*)
问题是,如果我添加这个接口,我会得到这个编译错误:
Error: The implementation useMap.ml does not match the interface useMap.cmi:
Values do not match:
val use :
DataStr.IntMap.key ->
(int * int) DataStr.IntMap.t -> (int * int) DataStr.IntMap.t
is not included in
val use : int -> DataStr.Couples.t -> DataStr.Couples.t
除了评论中的“冗余”接口之外,有没有办法编写一个让我做我想做的事情的接口?
【问题讨论】:
-
如果你封装了
(int * int) IntMap.t这个类型,在DataStr之外就没法知道那真的是一张地图。 -
我猜这个级别的封装并没有太大的作用。你想对客户端代码隐藏什么?
-
我只想显示“Couples”,同时隐藏“int2couple”和可能的“couple”,因为我创建它们只是为了使用它们
dataStr.ml。除了界面可读性之外,我想要获得什么没有特别的原因。 -
在这种情况下你可以暴露
module Couples = struct type t = (int * int) IntMap.t end。 -
是的,正如我所写,我想避免在定义 (
dataStr.ml) 和接口 (dataStr.mli) 中重复像(int * int) IntMap.t这样的语句,但我想这是唯一的选择。它仍然让我可以使用这些类型的 Map 为文件编写“更简单”的接口,例如val use : int -> Couples.t -> Couples.t。如果您愿意,可以将您的 cmets 合并到一个答案中,我会将其标记为最佳。我真的不相信会出现其他解决方案。谢谢你的建议!
标签: dictionary types interface ocaml