Поддерживает ли F# взаимозависимые классы в отдельных файлах?
Я работаю над IronJS, и один из наших исходных файлов становится очень длинным.
Сейчас я пытаюсь заставить работать.NET-взаимодействие. Я добавляю TryBinaryOperation
метод к Undefined
так что C# может использовать семантику JavaScript неопределенного значения.
Тем не менее, это вводит зависимость от Operators
тип, который вызывает круговую зависимость.
Runtime.fs:
type BoxedValue() =
struct
// Contains IsUndefined and get_Undefined, referencing the Undefined class, below.
...
and type Undefined() =
inherit DynamicObject()
...
override x.TryBinaryOperation(binder:BinaryOperationBinder, arg:obj, result:obj byref) : bool =
// Here, we are referencing BoxedValue, above.
result <- Operators.add(Und, BoxedValue.Box(arg))
true
...
Operators.fs:
type Operators =
...
// Here, we are referencing BoxedValue.
static member add(BoxedValue l, BoxedValue r)
...
Итак, у нас есть этот набор зависимостей:
Не определено, Не определено-> BoxedValue, Не определено-> Операторы, Операторы-> BoxedValue">
В идеале мы хотели бы разделить каждый из них на отдельный файл.
Возможно ли в F# иметь циклические зависимости между файлами?
1 ответ
Не существует прямого способа записи циклических зависимостей между типами, определенными в отдельных файлах (в текущей версии F#). В общем случае, способ решить проблему - это сломать одну из зависимостей и разрешить некоторую форму параметризации. Затем вы можете заполнить отверстие, чтобы построить круговую ссылку позже.
В вашем примере вы, вероятно, можете достаточно легко параметризовать Undefined
тип, чтобы взять ссылку на Operators
в качестве параметра. Если вам нужно больше функций, то вы можете использовать интерфейс. Только для одной функции (например, Operators.add
) вы можете написать что-то вроде этого:
and type Undefined() =
inherit DynamicObject()
...
// To be specified by code defined later
// (this can either be a function or an interface implementation)
static let mutable addition = (fun x y -> failwith "not initialized")
static member SetAddition(f) = addition <- f
override x.TryBinaryOperation
(binder:BinaryOperationBinder, arg:obj, result:obj byref) : bool =
// Here, we are referencing BoxedValue, above.
result <- addition(Und, BoxedValue.Box(arg))
true
Код в Operators.fs
обеспечит реализацию:
type Operators =
...
// Static constructor of the `Operators` type
static do Undefined.SetAddition(Operators.add)
....
// Here, we are referencing BoxedValue.
static member add(BoxedValue l, BoxedValue r)
Единственная сложность в том, что вам нужно убедиться, что статический конструктор Operators
будет вызван до Undefined
тип используется впервые. Это зависит от вашего конкретного случая, но обычно есть какой-то способ сделать это. (Возможно, есть какой-то основной тип, который может запустить инициализацию)