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

Другие вопросы по тегам