假设我在 F# 中有一个带有一些复杂类型约束的泛型类型:
[<Struct>]
type Vec2<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
X : 't
Y : 't
}
Run Code Online (Sandbox Code Playgroud)
现在我想创建另一个以此为基础的泛型类型:
// Does not work
[<Struct>]
type AABB<'t> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
Run Code Online (Sandbox Code Playgroud)
除非我复制类型约束,否则这不起作用:
[<Struct>]
type AABB<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
Run Code Online (Sandbox Code Playgroud)
这很快就会变老!
有没有办法将类型约束绑定到名称,以便我可以在整个代码中重用它们?
// Not real code
constraint IsNumeric 't =
't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)
[<Struct>]
type Vec2<'t when IsNumeric 't> =
{
X : 't
Y : 't
}
[<Struct>]
type AABB<'t when IsNumeric 't> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,一个合理的解决方法是创建一个表示约束的接口。这不会自动用作命名约束,但您可以定义一个辅助函数来捕获所需的操作,然后传递接口(以便您可以调用所需的操作)。
假设我们只需要加法和乘法:
type INumericalOps<'T> =
abstract Add : 'T * 'T -> 'T
abstract Mul : 'T * 'T -> 'T
[<Struct>]
type Vec2<'T> =
{ X : 'T
Y : 'T }
[<Struct>]
type AABB<'T, 'O when 'O :> INumericalOps<'T>> =
{ Min : Vec2<'T>
Max : Vec2<'T>
Ops : 'O }
Run Code Online (Sandbox Code Playgroud)
现在,该AABB类型还包含接口的实现INumericalOps,这比指定所有约束要短一些。*我们可以创建一个内联函数来捕获支持+这些类型的任何类型的实现:
let inline capture () =
{ new INumericalOps<_> with
member x.Add(a, b) = a + b
member x.Mul(a, b) = a * b }
Run Code Online (Sandbox Code Playgroud)
创建值时,类型推断将确保我们获得数值运算的正确实现:
let aabb =
{ Min = { X = 1.0; Y = 2.0 }
Max = { X = 1.0; Y = 2.0 }
Ops = capture() }
Run Code Online (Sandbox Code Playgroud)
小智 4
我相信这本质上是这个请求https://github.com/fsharp/fslang-suggestions/issues/641
简短的回答,现阶段 F# 不支持,但我认为添加它将是一个很棒的功能。