是否可以定义彼此依赖的类型并在分离的文件中定义?

时间:2010-10-21 00:56:28

标签: .net f# mutual-recursion fsyacc

我正在尝试实现具有扩展解析功能的库。我决定使用fsyacc,因为我从大学就知道了。不幸的是我遇到了以下问题

我为我的语法( Head )定义了一个类,并将其实现放在一个文件中。然后我将解析器定义为:

...
%start head
%type <Head> head
...

Fsyacc生成seeparated模块( Parser )。为了成功,必须按以下顺序进行编译:{{1​​}} Head.fs

为了使这个库与您在.NET中可以找到的类似,我想在 Head 中添加静态 Parse 方法。不幸的是,我需要使用 Parser 模块中的方法。

我知道可以使用“”运算符解决此类类型依赖项,但它仅适用于在一个文件中定义的类型。

有没有其他方法可以创建依赖于彼此的类型,即使它们位于不同的文件中?我正在寻找声明/实现分离机制,就像在C / C ++中那样,但我不能找不到任何东西。

4 个答案:

答案 0 :(得分:7)

简答:不。在F#2.0中,无法跨多个文件执行相互递归的实体。 (这是我们计划在下一版语言中解决的问题。)

您可以通过各种方式解决此问题,通常使用间接点和突变点。例如,你的Head类型可以有一个静态的'InitializeParser'方法,它将一个函数值发送到一个可变的全局变量中,然后在Head中定义的静态Parse方法可以通过那个可变的全局调用,并且在解析器实际定义之后,它可以去调用InitializeParser来调用值。(如果这没有意义,我可以更详细地说出来。)

答案 1 :(得分:3)

我希望有可能。在我阅读Brian的回复后,我开始寻找合适的解决方法。我不想强制库用户调用任何初始化方法。因此我想出了一些不同的东西。

如果编译器无法在编译时解析依赖项,我可以在运行时自己完成。 这是我的DepencendciesResolver的定义

module DependenciesResolver = 
    let GetMethod(_type, _method) =
        lazy (
            let a = System.Reflection.Assembly.GetExecutingAssembly()
            let t = a.GetType(_type)
            t.GetMethod(_method)
            )

在分隔文件中定义的类的示例:

A.fs

namespace MutualRecursion
type A() =
    static member _b = DependenciesResolver.GetMethod("MutualRecursion.B", "b")
    static member b() = A._b.Value.Invoke(null, [||])

B.fs

nameespace MutualRecursion
type B =
    static member b() = 
        printf "Called b()"

编译顺序为:A.fs B.fs

答案 2 :(得分:1)

你不能使用第三个文件解决这个问题,这个文件在这两个文件之后进行编译并使用新方法扩展Head吗?

答案 3 :(得分:1)

我会做类似以下的事情(我怀疑这正是Brian提出的建议)。请注意,用户不必进行任何棘手的初始化 - 类型本身知道如何“打结”。

<强> Head.fs

type IParser =
  abstract Parse : string -> int // or whatever
  ...

type Head() =
  static let mutable parser = Unchecked.defaultof<IParser>
  static member internal SetParser p = parser <- p
  member x.DoSomethingCool() =
    let i = parser.Parse("something to parse")
    ...

<强> Parser.fs

type Parser private () =
  static do
    Head.SetParser (Parser())
  interface IParser with
    member x.Parse s = 0
    ...