"F#"中的错误"A type instantiation involves a byref type."是什么意思?有什么解决方法?

6

我有一些封装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

我想做的是创建一个通用函数,可以直接传递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个回答

8

简短回答:请将您的mutable参数替换为ref


TA-Lib有一个非常不幸的API:那些讨厌的out参数(在F#中称为byref),它们总是会引起麻烦。在这种情况下,它们不能成为泛型类型实例化的一部分。

这里有一个更简短的示例。考虑一个好老的list<T>。我们可以创建一个空的list<int>

let noInts = [] : list<int>

但如果这些intbyref的呢?

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 。在通用实例化中,它们是被禁止的。很遗憾。


但有希望!

mutable 关键字只是在F#中创建可变单元的两种方法之一。另一种方式是使用 ref

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

这是一种老派的东西,早于mutable,作为库实现(而不是语言结构),在大多数情况下,mutable更好。但这不是其中之一!

事实证明:

  1. 与真正的.NET CIL out参数不同,ref单元可以很好地成为泛型实例化的一部分。因为从.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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接