简短回答:替换你的mutable
参数与ref
.
TA-Lib 有一个非常不幸的 API:那些讨厌的 APIout
-参数(在 F# 中称为byref
),他们总是惹麻烦。在这种情况下,它们不能是泛型类型实例化的一部分。
这是一个更短的例子。考虑一个好的旧list<T>
。我们可以做一个空的list<int>
:
let noInts = [] : list<int>
但如果那些怎么办int
s are byref
?
let noRefs = [] : list< byref<int> >
编译器说,无能为力。类型实例化涉及 byref 类型。 Common IL 规则不允许这样做。 Sorry.
在你的情况下,最后一个参数myGenericFunction
是一个 F# 函数。在 F# 中,函数由类型表示FSharpFunc<T, R>
(where T
是论证并且R
是结果)。所以你最后一个参数的类型是这样的:
FSharpFunc< int * int * float array * int * byref<int> * byref<int> * float array, int >
看到那两个byref<int>
在里面吗?那些是&outStartIndex
and &outNbElement
。并且它们在通用实例化中是被禁止的。倒霉。
但还有希望!
The mutable
关键字只是在 F# 中创建可变单元格的两种方法之一。另一种方法是ref
:
let x = ref 0 // Initialization
printfn "%d" !x // Prints "0"
x := 42 // Mutation
printfn "%d" !x // Prints "42"
这是一个老派的事情,早于mutable
,作为库实现(而不是语言构造),并且在大多数情况下mutable
更好。但这不是其中之一!
事实证明:
- 与真正的 .NET CIL 不同
out
-参数,ref
单元格可以成为通用实例化的一部分就好了。因为,从 .NET 的角度来看,它们没有什么特别的——只是另一个类。
- 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