为 F# 中的一组类型约束指定名称?

sdg*_*sdh 8 f#

假设我在 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)

Tom*_*cek 5

在这种情况下,一个合理的解决方法是创建一个表示约束的接口。这不会自动用作命名约束,但您可以定义一个辅助函数来捕获所需的操作,然后传递接口(以便您可以调用所需的操作)。

假设我们只需要加法和乘法:

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# 不支持,但我认为添加它将是一个很棒的功能。