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

标签 f# cyclic-dependency

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

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

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

但这对我来说行不通。

此外,我错误地认为签名文件可以帮助我解决这个问题,但它们的行为与 C++ 不同

  • 它们用于定义函数/类型访问模式(内部/公共(public)),也可以在此处添加函数/类型注释头。

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

可能有人有不同的想法?

最佳答案

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

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

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

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

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

总之,这些只是一些通用的技巧。如果不了解您正在使用的类型,很难给出更准确的建议。如果您可以分享更多详细信息,也许我们可以找到一种方法来避免某些递归引用。

关于f# - F# 中不同文件的类型和函数之间的循环依赖问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5404735/

相关文章:

c - 将多个头文件链接到c文件

java-8 - Java 8 的循环依赖分析 - 用于自动构建

css - 处理百分比大小的盒子CSS的循环依赖(特别是如何获得最大高度)

types - # 符号在 f# 函数签名中是什么意思?

f# - 与类型注释的模式匹配

visual-studio-2010 - F# 3.0 是否可用于 Visual Studio 2010 专业版?

C++ 类的循环依赖(单例)

python - 循环导入以修复 pylint 中的 R0401

authentication - F#/FAKE NuGet 私有(private)源身份验证

f# - 在不同的泛型实例中实现相同的接口(interface)