Можно ли определить типы, которые зависят друг от друга и определены в разделенных файлах?

StackOverflow https://stackoverflow.com/questions/3983509

Вопрос

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

Если компилятор не может разрешить зависимости во время компиляции, я могу сделать это самостоятельно во время выполнения. Вот определение моего Depencendciesresolver

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

И пример классов, определенных в разделенных файлах:

А.Ф.

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

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

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

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
    ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top