我有两个模块,Graph和Game,其他模块对其进行了参数设置。它们还包含f和g函数,当我在测试模块中使用它们时会导致类型检查问题。我遗漏了很多对这个问题不重要的代码。
这里是具有某些模块AbstractUSet
的Graph模块。原始代码中经常使用AbstractUSet.t
。函数f
随后将使用另一个函数并进行一些工作。问题是,另一个功能来自另一个模块,并且具有不同的类型。
module UTYPE = sig
type t
val compare : t -> t -> int
end
module type GRAPH = sig
module U : UTYPE
module AbstractUSet : Set.S
val f : (AbstractUSet.t -> AbstractUSet.t) -> AbstractUSet.t -> AbstractUSet.t
end
module Graph (UA : UTYPE) : (GRAPH with module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(struct type t=U.t let compare=U.compare end)
let f g uset = g uset
end
另一个模块是Game
模块。它用AbstractVSet.t
做很多事情。它包含功能g
,稍后应从f
模块为功能Graph
输入。
module type GAME_PIECE = sig
type t
val compare : t -> t -> int
end
module type GAME = sig
module P : GAME_PIECE
module AbstractVSet : Set.S
val g : AbstractVSet.t -> AbstractVSet.t
end
module GameInstance (NA : GAME_PIECE) : (GAME with module P=NA) = struct
module P = NA
module AbstractVSet = Set.Make(struct type t=P.t let compare=P.compare end)
let g vset = vset
end
这是我的测试模块。最后,UTYPE
和GAME_PIECE
相同,但是我无法明确地告诉OCaml。我评论了不进行类型检查的行。编译器说MyGame.AbstractVSet.t
和MyGraph.AbstractUSet.t
之间存在冲突。
module TestGame = struct
include(Game(struct type t=string let compare=compare end))
end
module TestGraph = struct
include(Graph(struct type t=string let compare=compare end))
end
module Test = struct
module MyGame = TestGame
module MyGraph = TestGraph
let stringlist = ["a";"b"]
let uset = MyGraph.uset_from_ulist stringlist // works fine
let vset = MyGame.vset_from_vlist stringlist // works fine
let result = MyGraph.f (MyGame.g) vset // does not typecheck!
end
如果您要问,为什么我要使用这么多模块:该项目比此代码摘录大很多,并且它打算像这样;)谁能帮助我让OCaml编译器清楚UTYPE
模块中的GAME_PIECE
和Test
相同吗?非常感谢您的帮助!
第一个问题是您的抽象集模块是不同:仅当将函子应用程序应用于完全相同的named模块时,OCaml中两个函子应用程序的结果才相等。例如,在
module A = struct type t = int end
module F(X:sig type t end) = struct type t end
module FA = F(A)
module B = A
module FA' = F(B)
module C = F(struct type t end)
FA.t
和Fa'.t
的类型相同
let f (x:FA.t): FA'.t = x
但是类型C.t
和FA.t
不同:
let f (x:C.t): FA'.t = x
Error: This expression has type C.t but an expression was expected of type
FA'.t
但是当不需要它们时,可以通过不使用匿名结构来解决此部分:
module Graph (UA : UTYPE) : (GRAPH with module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(U)
let f g uset = g uset
end
然后,剩下的是Game(M).AbstractUSet
和Graph(M).AbstractUSet
定义了两种不同的类型。 (请注意,这可能是测试之外的正确行为)。对于测试,一种选择是简单地公开那些模块是函子应用程序的结果的信息。例如,可以将GAME
模块类型(和GameInstance
仿函数)重新定义为:
module type GAME = sig
type set
module P : GAME_PIECE
module AbstractVSet: Set.S with type t = set
val g : AbstractVSet.t -> AbstractVSet.t
end
module GameInstance (NA : GAME_PIECE) :
(GAME with type set:= Set.Make(NA).t and module P=NA) = struct
module P = NA
module AbstractVSet = Set.Make(NA)
let g vset = vset
end
[这里,我们提示GameInstance(M).AbstractVSet.t
是与Set.Make(M).t
相同的类型。
结合图部分的相同操作:
module type GRAPH = sig
type set
module U : UTYPE
module AbstractUSet : Set.S with type t = set
val f : (AbstractUSet.t -> AbstractUSet.t) -> AbstractUSet.t -> AbstractUSet.t
end
module Graph (UA : UTYPE) :
(GRAPH with type set := Set.Make(UA).t and module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(UA)
let f g uset = g uset
end
我们保留了足够的类型信息以保持测试的相等性:
module TestGame = GameInstance(String)
module TestGraph = Graph(String)
module Test = struct
module MyGame = TestGame
module MyGraph = TestGraph
let result vset = MyGraph.f (MyGame.g) vset (* does typecheck *)
end