将类型约束添加到派生类型F#(此代码不够通用)

mat*_*wko 5 generics polymorphism f# type-constraints

我有这个界面:

type IMovingFunc< 'T > =
    abstract member Add : 'T -> unit
Run Code Online (Sandbox Code Playgroud)

现在我想创建一个实现Add函数并使用(+)运算符的泛型类型:

type MovingSum< ^T >(initial : ^T) =
    let mutable sum = initial

    interface IMovingFunc< ^T> with
        member inline this.Add x = sum <- sum + x
Run Code Online (Sandbox Code Playgroud)

不幸的是,我收到了这个错误:

此代码不够通用.当^ T :(静态成员(+):^ T*^ T - > ^ T)时,类型变量^ T无法推广,因为它会逃避其范围.

我已经尝试向MovingSum添加类型约束,但它没有帮助:

type MovingSum< ^T when ^T : (static member ( + ) : ^T * ^T -> ^T)>(initial : ^T) 
Run Code Online (Sandbox Code Playgroud)

你能告诉我如何解决这个问题还是有其他方法来实现这个目标?

kvb*_*kvb 6

托马斯的回答很好.然而,对于完整性:它可能的类型使用静态解析类型参数:

type MovingSum< ^T when ^T : (static member (+) : ^T * ^T -> ^T)>(initial : ^T) =
    let mutable sum = initial

    member inline this.Add(x:^T) = sum <- sum + x

let m = MovingSum(1) in m.Add(3) // works!
Run Code Online (Sandbox Code Playgroud)

但请注意:

  1. 成员约束需要包含在声明参数的位置.
  2. 我在这里收到一些虚假的警告,表明这种语言功能可能还没有完全成熟......
  3. 无论如何,这对你没有帮助,因为接口成员不能内联 - 给定接口类型的值,编译器没有任何方法来确定实现接口的运行时类型,因此不可能知道什么实现内联.


Tom*_*cek 5

正如Fyodor在评论中所提到的那样,类型不能具有与函数相同的静态解析约束(主要是因为静态约束是使用内联处理的,而您不能真正内联整个类型)。

解决此问题的一种方法是使约束在类型中显式,然后创建具有静态成员约束的函数,该函数捕获功能并将其传递给类型。

在您的示例中,您需要+运算符,因此我们可以添加adder以下类型的参数:

type MovingSum<'T>(initial:'T, adder:'T -> 'T -> 'T) =
    let mutable sum = initial
    interface IMovingFunc<'T> with
        member this.Add x = sum <- adder sum x
Run Code Online (Sandbox Code Playgroud)

这不需要静态成员约束,但是在创建时需要提供额外的参数MovingSum。这还不错,但是您可以通过定义一个内联函数来创建MovingSum和捕获+运算符来避免这种情况:

let inline movingSum initial = 
  MovingSum(initial, fun a b -> a + b) :> IMovingFunc<_>
Run Code Online (Sandbox Code Playgroud)