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

扶醉桌前 提交于 2019-12-05 14:23:24

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