我目前的项目使用AST有40种不同类型(被破坏的联合),并且这种AST中的几种类型具有循环依赖性.类型不是那么大,因此我将它们放在一个文件中,并type ... and ...为相互依赖的类型应用构造.
现在,我正在添加函数以在AST中的每个元素下进行一些计算.因为,有许多函数有几行代码,为了使源代码更清晰,我将这些函数分离到不同的文件中.
在没有循环依赖的情况下也可以,当依赖函数在同一个文件中时也可以使用 - 在这种情况下我可以使用let rec function1 ... and function2 ...构造
但它不适用于我的情况.
我也错误地认为,签名文件可以帮助我,但它们的行为不同于C++ - 它们用于定义函数/类型访问模式(内部/公共),还可以在这里添加函数/类型注释标题...
我看到的唯一可能的解决方案是将所有函数移动到一个文件并使用let rec ... and ... and ... and ... and ...构造
可能有人有不同的想法?
提前致谢.
正如评论中所提到的,没有办法在多个文件之间拆分具有循环依赖关系的函数(或类型).签名文件主要用于文档目的,因此它们无济于事.
在不知道依赖性究竟是什么的情况下很难给出一些建议.但是,可以使用函数或接口重构实现的某些部分.例如,如果您有:
let rec process1 (a:T1) =
match a with
| Leaf -> 0
| T2Thing(b) -> process2 b
and process2 (b:T2) =
match b with
| T1Thing(a) -> process1 a
Run Code Online (Sandbox Code Playgroud)
您可以修改该函数process1以将第二个函数作为参数.这使得可以在两个文件之间拆分实现,因为它们不再相互递归:
// File1.fs
let process1 (a:T1) process2 =
match a with
| Leaf -> 0
| T2Thing(b) -> process2 b
// File2.fs
let rec process2 (b:T2) =
match b with
| T1Thing(a) -> process1 a process2
Run Code Online (Sandbox Code Playgroud)
如果你能找到一些更清晰的结构 - 例如两个包含逻辑相关功能并需要相互访问的功能块,那么你也可以定义一个接口.对于只有两个函数的示例,这没有多大意义,但它看起来像这样:
type IProcess2 =
abstract Process : T2 -> int
let process1 (a:T1) (process2:IProcess2) =
match a with
| Leaf -> 0
| T2Thing(b) -> process2.Process b
let rec process2 (b:T2) =
let process2i =
{ new IProcess2 with
member x.Process(a) = process2 a }
match b with
| T1Thing(a) ->
process1 a process2i
Run Code Online (Sandbox Code Playgroud)
无论如何,这些只是一些通用技术.如果不了解您正在使用的类型,就很难提供更精确的建议.如果您可以分享更多细节,也许我们可以找到一种方法来避免一些递归引用.