Можно ли определить типы, которые зависят друг от друга и определены в отдельных файлах? - PullRequest
4 голосов
/ 21 октября 2010

Я пытаюсь реализовать библиотеку с расширенными возможностями синтаксического анализа.Я решил, что буду использовать fsyacc, потому что знал об этом в университете.К сожалению, я столкнулся со следующей проблемой.

Я определил класс для заголовка моей грамматики ( Head ) и поместил его реализацию в один файл.Затем я определил синтаксический анализатор как:

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

Fsyacc генерирует разделенный модуль ( Parser ).Чтобы добиться успеха, он должен быть скомпилирован в следующем порядке: Head.fs Parser.fs

Чтобы сделать эту библиотеку похожей на ту, которую вы можете найти в .NET, я хотел бы добавить статический Разобрать метод до Head .К сожалению, мне нужно было бы использовать методы из модуля Parser .

Я знаю, что такие зависимости типа можно решить с помощью ' и 'оператор, но он применим только к типам, определенным в одном файле.

Есть ли другой способ создания типов, которые зависят друг от друга, даже если они находятся в отдельных файлах? Я искал механизм разделения объявления / реализации, подобный тому, который был в C / C ++, ноЯ ничего не смог найти.

Ответы [ 4 ]

7 голосов
/ 21 октября 2010

Краткий ответ: нет. В F # 2.0 нет способа создавать взаимно рекурсивные сущности для нескольких файлов. (Это то, что мы планируем рассмотреть в следующей версии языка.)

Вы можете обойти это различными способами, обычно используя точку косвенного обращения и мутации. Например, ваш тип Head может иметь статический метод InitializeParser, который помещает значение функции в изменяемую глобальную переменную, а затем статический метод Parse, определенный в Head, может вызываться через этот изменяемый глобал, и после того, как анализатор фактически определен, может пойти и вызвать InitializeParser, чтобы вставить значение. (Если это не имеет смысла, я могу объяснить это более подробно.)

3 голосов
/ 21 октября 2010

Я надеялся, что это возможно. После прочтения ответа Брайана я начал искать правильный обходной путь. Я не хотел заставлять пользователей библиотеки вызывать какие-либо методы инициализации. Поэтому я придумал что-то другое.

Если компилятор не может разрешить зависимости во время компиляции, я могу сделать это самостоятельно во время выполнения. Вот определение моего 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

1 голос
/ 21 октября 2010

Я бы сделал что-то вроде следующего (что я подозреваю, примерно то, что предлагал Брайан).Обратите внимание, что пользователь не должен делать хитрую инициализацию - сами типы знают, как «завязать узел».

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
    ...
1 голос
/ 21 октября 2010

Не можете ли вы обойти это с 3-м файлом, который компилируется после этих двух и расширяет Head новым методом?

...