Что является ошибкой «Реализация типа включает в себя тип byref». и что такое обходной путь в F # - PullRequest
1 голос
/ 26 февраля 2020

У меня есть несколько оберток кода TA-Lib, и многие обертки очень похожи:

let sma (timePeriod: int) (data: float[]) =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = Core.Sma(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, smaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding smaData



let ema (timePeriod: int) (data: float[]) =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0
    let mutable emaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = Core.Ema(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, emaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding emaData

Я хотел бы создать обобщенную функцию c, в которой я могу просто передать TA-Lib функция для вызова. Что-то вроде:

let myGenericFunction (timePeriod: int) (data: float[]) TALibFunc =
    let mutable outStartIndex = 0
    let mutable outNbElement = 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = TALibFunc(0, (data.Length - 1), data, timePeriod, &outStartIndex, &outNbElement, smaData)

    if retCode <> Core.RetCode.Success then
        invalidOp (sprintf "AssertRetCodeSuccess")

    let padding = Array.create (timePeriod - 1) System.Double.NaN
    Array.append padding smaData

, но я получаю ошибку:

[FS0412] В экземпляре типа используется тип byref. Это не разрешено правилами Common IL.

Есть ли обходной путь для этого? Я не знаком с этой проблемой.

1 Ответ

3 голосов
/ 26 февраля 2020

Краткий ответ: замените ваши mutable параметры на ref.


У TA-Lib очень неудачный API: эти надоедливые out -параметры (известны в F # как byref) они всегда создают проблемы. В этом случае они не могут быть частью общего экземпляра типа c.

Вот гораздо более короткий пример. Возьмем старый добрый list<T>. Мы можем сделать пустой list<int>:

let noInts = [] : list<int>

Но что, если эти int s byref?

let noRefs = [] : list< byref<int> >

Нет, не может сделать, - говорит компилятор. Реализация типа включает в себя тип byref. Это не разрешено правилами Common IL. Извините.


В вашем случае последний параметр myGenericFunction является функцией F #. В F # функции представлены типом FSharpFunc<T, R> (где T - аргумент, а R - результат). Итак, тип вашего последнего параметра таков:

FSharpFunc< int * int * float array * int * byref<int> * byref<int> * float array, int >

Видите эти два byref<int> там? Это &outStartIndex и &outNbElement. И они запрещены в общем c экземпляре. Неудача.


Но надежда есть!

Ключевое слово mutable - это только один из двух способов сделать изменяемые ячейки в F #. Другой способ - ref:

let x = ref 0     // Initialization
printfn "%d" !x   // Prints "0"
x := 42           // Mutation
printfn "%d" !x   // Prints "42"

Это старая школа, предшествующая mutable, реализованная как библиотека (в отличие от языковой конструкции), и в большинстве случаев mutable лучше. Но это не один из таких случаев!

Оказывается, что:

  1. В отличие от true. NET CIL out -параметры, ref ячейки могут быть частью c экземпляр просто отлично. Потому что, с точки зрения. NET, они не являются чем-то особенным - просто другой класс.
  2. У компилятора F # есть специальный соус для них: когда ожидаемый тип - ref, но вы пытаясь передать функцию с out -параметром на своем месте, компилятор автоматически сгенерирует для вас какой-нибудь код переноса.

Итак, вооружившись этим знанием, вы можете изменить myGenericFunction как это:

let myGenericFunction (timePeriod: int) (data: float[]) TALibFunc =
    let outStartIndex = ref 0
    let outNbElement = ref 0

    let mutable smaData : float array = Array.zeroCreate (data.Length - timePeriod + 1)

    let retCode = TALibFunc(0, (data.Length - 1), data, timePeriod, outStartIndex, outNbElement, smaData)

    ...

И тогда потребители могут назвать это так:

myGenericFunction 42 [|1; 2; 3|] Core.Sma // Wrapping code gets generated here
...