使用两个参数化模块时的OCaml类型检查问题

问题描述 投票:0回答:1

我有两个模块,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

这是我的测试模块。最后,UTYPEGAME_PIECE相同,但是我无法明确地告诉OCaml。我评论了不进行类型检查的行。编译器说MyGame.AbstractVSet.tMyGraph.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_PIECETest相同吗?非常感谢您的帮助!

ocaml q typechecking
1个回答
0
投票

第一个问题是您的抽象集模块不同:仅当将函子应用程序应用于完全相同的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.tFa'.t的类型相同

let f (x:FA.t): FA'.t = x

但是类型C.tFA.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).AbstractUSetGraph(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
© www.soinside.com 2019 - 2024. All rights reserved.