开发者

Is it possible to define types that depend on each other and are defined in separated files?

I am trying to implement a library with extended parsing capabilities. I decided that I will use fsyacc because I knew it from the university. Unfortunately I encountered following problem.

I defined a class for the head of my grammar (Head) and place its implementation in a single file. Then I defined parser as:

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

Fsyacc generates seeparated module (Parser). In order to succeed 开发者_StackOverflow中文版it has to be compiled in following order: Head.fs Parser.fs

In order to make this library similar to what you can find in .NET I would like to add a static Parse method to Head. Unfortunately I would need to make use of methods from Parser module.

I know that such type dependencies can be solved with 'and' operator but it is only applicable to types defined in one file.

Is there any other way to create types that depend on each other even when they are in separate files? I was looking for declaration/implementation separation mechanism like the one in C/C++ but I couldn't find anything.


Short answer: no. There's no way to do mutually recursive entities across multiple files in F# 2.0. (This is something we plan to address in the next version of the language.)

You can work around this in a variety of ways, typically using a point of indirection and mutation. For example, your Head type could have a static 'InitializeParser' method that pokes a function value into a mutable global variable, and then the static Parse method defined in Head could call via that mutable global, and after the parser is actually defined, it can go and call InitializeParser to poke the value in. (If that doesn't make sense, I can spell it out in more detail.)


I was hoping that it is possible. After I read Brian's reply I started looking for a proper workaround. I didn't want to force library users to call any initialization methods. Therefore I came up with something diffrent.

If compiler cannot resolve dependencies at compile-time I can do it on my own at run-time. Here is definition of my DepencendciesResolver

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

And example of classes defined in separated files:

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()"

Compilation order is: A.fs B.fs


Can't you work around this with a 3rd file which compiles after these two and extends Head with the new method?


I'd do something like the following (which I suspect is roughly what Brian was proposing). Note that the user doesn't have to do any tricky initialization - the types themselves know how to "tie the knot".

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
    ...
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜