F#中不同文件的类型和函数之间的循环依赖问题

时间:2011-03-23 11:46:54

标签: f# cyclic

我当前的项目使用了具有40种不同类型的AST(被破坏的联合),并且来自此AST的几种类型具有循环依赖性。 类型不是很大,因此我将它们放在一个文件中,并为相互依赖的类型应用type ... and ...构造。

现在,我正在添加函数以在AST中的每个元素下进行一些计算。 因为,有许多函数有几行代码,为了使源代码更清晰,我将这些函数分离到不同的文件中。

在没有循环依赖的情况下没问题,当依赖函数在同一个文件中时也可以 - 在这种情况下我可以使用let rec function1 ... and function2 ...构造

但在我的情况下它不起作用。

我也错误地认为,签名文件可以帮助我解决这个问题,但是他们的行为与C ++不同 - 它们用于定义函数/类型访问模式(内部/公共),还可以在这里添加函数/类型注释标题...

我看到唯一可行的解​​决方案 - 将所有函数移动到一个文件并使用let rec ... and ... and ... and ... and ...构造

可能有人有不同的想法吗?

提前致谢。

1 个答案:

答案 0 :(得分:6)

如评论中所述,无法在多个文件之间拆分具有循环依赖关系的函数(或类型)。签名文件主要用于文档目的,因此它们无济于事。

如果不知道依赖是什么,很难给出一些建议。但是,可以使用函数或接口重构实现的某些部分。例如,如果您有:

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

您可以修改函数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

如果你能找到一些更清晰的结构 - 例如两个功能块包含逻辑相关的功能,需要相互访问,然后您还可以定义一个接口。对于只有两个函数的示例,这没有多大意义,但它看起来像这样:

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

无论如何,这些只是一些通用技术。如果不了解您正在使用的类型,就很难提供更精确的建议。如果您可以分享更多细节,也许我们可以找到一种方法来避免一些递归引用。