Можно ли определить типы, которые зависят друг от друга и определены в разделенных файлах?
-
09-10-2019 - |
Вопрос
Я пытаюсь внедрить библиотеку с расширенными возможностями анализа. Я решил, что буду использовать 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
...