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

独自空忆成欢 提交于 2019-12-22 08:18:54

问题


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


回答1:


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




回答2:


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




回答3:


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




回答4:


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


来源:https://stackoverflow.com/questions/3983509/is-it-possible-to-define-types-that-depend-on-each-other-and-are-defined-in-sepa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!