Поддерживает ли F # взаимозависимые классы в отдельных файлах? - PullRequest
10 голосов
/ 04 июля 2011

Я работаю над 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)
        ...

Итак, у нас есть этот набор зависимостей:
imageUndefined, Undefined->BoxedValue, Undefined->Operators, Operators->BoxedValue">

В идеале мы хотели бы разбить каждый из них на отдельный файл.

Возможно ли в F # иметь циклические зависимости между файлами?

1 Ответ

6 голосов
/ 04 июля 2011

Не существует прямого способа записи циклических зависимостей между типами, определенными в отдельных файлах (в текущей версии 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 будет использован впервые. Это зависит от вашего конкретного случая, но обычно есть какой-то способ сделать это. (Вероятно, есть некоторый основной тип, который может запустить инициализацию)

...