我有一些代码包装 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
Run Code Online (Sandbox Code Playgroud)
我想做的是创建一个通用函数,我可以在其中传递 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
Run Code Online (Sandbox Code Playgroud)
但我得到的错误是:
[FS0412] 类型实例化涉及 byref 类型。Common IL 规则不允许这样做。
有解决方法吗?我对这个问题不太熟悉。
简短的回答:将您的mutable参数替换为ref.
TA-Lib 有一个非常不幸的 API:那些讨厌的out参数(在 F# 中称为byref),它们总是带来麻烦。在这种情况下,它们不能是泛型类型实例化的一部分。
这是一个更短的例子。考虑好老了list<T>。我们可以做一个空的list<int>:
let noInts = [] : list<int>
Run Code Online (Sandbox Code Playgroud)
但如果这些int是呢byref?
let noRefs = [] : list< byref<int> >
Run Code Online (Sandbox Code Playgroud)
编译器说,无能为力。类型实例化涉及 byref 类型。Common IL 规则不允许这样做。对不起。
在您的例子中, 的最后一个参数myGenericFunction是 F# 函数。在 F# 中,函数由类型表示FSharpFunc<T, R>(其中T是参数,R是结果)。所以最后一个参数的类型是这样的:
FSharpFunc< int * int * float array * int * byref<int> * byref<int> * float array, int >
Run Code Online (Sandbox Code Playgroud)
看到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"
Run Code Online (Sandbox Code Playgroud)
这是一个老派的东西,早于mutable,作为一个库实现(而不是语言构造),并且在大多数情况下mutable更好。但这不是其中之一!
事实证明:
out参数不同,ref单元格可以成为通用实例化的一部分。因为,从 .NET 的角度来看,它们没有什么特别的——只是另一个类。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)
...
Run Code Online (Sandbox Code Playgroud)
然后消费者可以这样称呼它:
myGenericFunction 42 [|1; 2; 3|] Core.Sma // Wrapping code gets generated here
Run Code Online (Sandbox Code Playgroud)