模块依赖性周期



我有:

模块1

  • 提供类型Module1.type1,其构造函数以及一些接受和返回type1
  • 的功能

模块2

  • open Module1
  • open Module3
  • 提供类型Module2.type2,还具有接受type1type3作为params
  • 的功能

模块3

  • open Module1
  • open Module2
  • 提供依赖type1
  • Module3.type3类型及其构造函数
  • 提供接受和返回类型type1type2type3
  • 的功能

问题

因此,我显然会收到编译器的dependency cycle: src/Module3.cmj -> src/Module2.cmj -> src/Module3.cmj错误。在与单个导入的打字稿/Js中可以实现的东西是不可能实现的。如何解决这个问题?

我真的不想更改程序的体系结构,只是为了促进编译器/模块系统的缺点。

处理问题的最简单方法确实是递归模块。我不建议您使用它们,因为递归模块可以使您的代码更难读取,编译,并且在最复杂的情况下可以在运行时破坏您的代码。更不用说您在模块定义中使用副作用(请不要(。

我将使用OCAML语法,您应该能够轻松地翻译成理性。

如果您无论如何都想使用它,这里是快速而肮脏的解决方案,使用递归模块和函子。

快速而肮脏的解决方案

1(创建一个模块mymodtypes,该模块将指示模块2和模块3的预期类型。它应该看起来像:

module type Module2type = sig ... end
module type Module3type = sig ... end

...是模块的预期签名(如果您已经写了接口文件,只需在此处复制/粘贴它们,如果您不写这些,则很重要(

2(将模块2和模块3放入函子中,期望其他模块

例如,Module2的代码现在看起来像

module MakeModule2(Module3 : MyModTypes.Module3type) = struct
(* the code of module2 *)
end

模块3的代码将以相同的方式,只需在添加行中交换2和3。

3(用该代码创建一个模块makemodules2and3(转换为理性(:

module rec Module2 : MyModTypes.Module2type = Module2.MakeModule2(Module3)
and Module3 : MyModTypes.Module3type = Module3.MakeModule3(Module2)

请注意,递归模块定义总是期望模块类型。

4(Module2Module3的随后使用现在应该在能够使用它们之前open Makemodules2and3

正确的解决方案

您必须更改程序的体系结构。略微。

正如OP所说,功能没有依赖性的循环,这是一种缓解。只需将模块2和模块3分为两个新模块即可。一个仅取决于模块1及其模块的功能,一个具有"下一步"功能。

这是一种更好的方法来宣布模块的方式:它们应该是定义的类型的方法。理想情况下,您为每种类型都有一个模块,以及类型之间的每种相互作用的附加模块。

看起来Module1不取决于其他两个模块。您可以保持原样。但是,由于其他两个是相互递归的,因此您可以使用递归模块语法表达。尽管您在定义点声明了模块签名,但这确实有一个要求,因为理性需要知道期望什么。例如:

/* Impl.re */
module rec Module2: {
  type type2;
  let make: (Module1.type1, Module3.type3) => type2;
} = {
  ... actual implementation of Module2 ...
} and Module3: {
  type type3;
  let make: Module1.type1 => type3;
  let foo: Module1.type1;
  let bar: Module2.type2 => type3;
} = {
  ... actual implementation of Module3 ...
};

这是您要使用的一般形状,您可以根据需要调整它。

如果您不希望用户必须执行Impl.Module2....访问递归模块,甚至可以使用include将其曝光为文件模块:

/* Module2.re */
include Impl.Module2;

,您甚至可以通过编译时间警告来注释实现模块(Impl.Module2和3(,以使用户知道不要使用这些模块:

/* Impl.re */
[@deprecated "Please use Module2 instead of Impl.Module2"]
module Module2: {...} = {...};

最新更新