type VBO<'T when 'T : (new : unit -> 'T) and 'T : struct and 'T :> ValueType> =
{ Handle : int
target : BufferTarget
size : int
dataSize : int
data : 'T []
pos : int
usage : BufferUsageHint }
type VBO =
static member Create(target, size, pos, usage, (data : Vector3 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
// Type mismatch. Expecting Vector3 but found Vector2
static member Create(target, size, pos, usage, (data : Vector2 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
// This construct causes code to be less generic than indicated by the type annotations.
// The type variable 'T has been constrained to be type 'Vector3'.
static member CreateImpl(target, size, pos, usage, dataSize, (data : 'T [])) =
let h = GL.GenBuffer()
{ Handle = h
target = target
size = size
dataSize = dataSize
data = data
pos = pos
usage = usage }
F#尝试约束我的代码,但我希望它是通用的。我真的不关心数据的类型,我只需要传递正确的dataSize。
我做错了什么?
答案 0 :(得分:5)
如果F#编译器在同一代码块中的特定实例中使用,它们似乎专门用于泛型类型。尝试拆分你的声明:
type VBO<'T when 'T : (new : unit -> 'T) and 'T : struct and 'T :> ValueType> =
{ Handle : int
target : BufferTarget
size : int
dataSize : int
data : 'T []
pos : int
usage : BufferUsageHint }
type VBO =
static member CreateImpl(target, size, pos, usage, dataSize, (data : 'T [])) =
let h = GL.GenBuffer()
{ Handle = h
target = target
size = size
dataSize = dataSize
data = data
pos = pos
usage = usage }
type VBO with
static member Create(target, size, pos, usage, (data : Vector3 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
static member Create(target, size, pos, usage, (data : Vector2 [])) =
VBO.CreateImpl(target, size, pos, usage, Vector2.SizeInBytes, data)
答案 1 :(得分:3)
F#的类型推理从上到下和从左到右进行,偶尔会导致有趣的角落情况。特别是,类型中的重新排序方法定义可以影响推断类型。正如Ganesh指出的那样,你的问题的一个解决方案是将你的类型分解为不相交的块,但这实际上并不是必需的 - 只需将泛型方法放在第一位就足够了(并注意你也可以删除类型注释) data
,如果需要的话。)
您可以看到与简单let rec
绑定非常相似的行为。考虑:
let rec f() = h 5
and g() = h "test"
and h x = x
在尝试推断f
的类型时,编译器会注意h
必须将int
作为输入,这会使g
的定义无效。重新排序定义以放置h
是最简单的解决方案(然后编译器可以在查看h
和f
的主体之前推断出g
的泛型类型,所以一切经历好的)。
或者,您可以显式地使h
泛型并添加类型注释以帮助编译器输出:
let rec f() = h 5
and g() = h "test"
and h<'t> (x:'t) :'t = x
这可能不方便,因为它可能需要大量的注释开销,但在某些情况下,简单的重新排序定义不足以让编译器推断出正确的类型,因此有时可能是必要的。
答案 2 :(得分:2)
我认为最好查看代码以了解出现了什么问题。
所以这是一个更简单的例子,显示了同样的问题。
type test =
static member test (data:int) = test.actual(data)
static member test (data:float) =test.actual(data)
static member actual (data:'t) = ()
问题在于,对于静态成员函数,需要知道所有类型 - 或者您需要泛型类型。
我认为最简单的解决方案是将代码更改为
let actual (data:'t) = ()
type test =
static member test (data:int) = actual(data)
static member test (data:float) =actual(data)
在这里,编译器可以更自由地更改let绑定以使其成为通用。