type-parameter支持添加的通用接口

时间:2013-03-26 13:07:59

标签: generics f#

我在F#中尝试创建一个通用接口,其中类型参数必须支持添加,所以我最终得到这样的东西:

type IMyInterface<'b when 'b : (static member (+) : 'b * 'b -> 'b)> =
    abstract member myFunction : 'b -> 'b

不幸的是,这段代码给了我以下错误:

  

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

我发现this question有关同一问题,但我不确定为什么该功能必须标记为inline

另外,既然你不能创建一个抽象成员inline,那么在我的具体例子中是否存在另一种解决方案?或者我是否必须找到另一种方法来实现这一目标?

2 个答案:

答案 0 :(得分:7)

首先,我想我不完全理解为什么你需要声明接口的类型参数支持添加 - 我想在MyFunction的实现中会使用添加,在这种情况下调用者接口不需要知道它。如果要公开添加,只需将其作为单独的方法添加:

type IMyInterface<'T> =
  abstract Add : 'T * 'T -> 'T
  abstract MyFunction : 'T -> 'T

我认为静态成员约束在inline函数或inline静态成员之外的其他地方不能很好地工作(但我可能错了)。如果需要在泛型类型中使用数值运算,则可以使用捕获实现的技巧(在inline方法中)并将它们存储在接口中。我的blog about generic math in F#结尾处有一个例子。

诀窍是使用您需要的数字操作定义一个接口,并将其作为附加参数传递给构造函数:

type IAddition<'T> =
  abstract Add : 'T * 'T -> 'T

type MyType<'T>(a:'T, b:'T, add:IAddition<'T>) =
  member x.Sum = add.Add(a, b)

到目前为止,这是以标准.NET方式使用接口 - 接口表示数字操作,我们通过接口调用它们。现在,诀窍是添加inline方法Create,它只接受两个参数,并需要+作为静态约束。然后,该方法可以实现接口并将其作为最后一个参数传递给MyType

  static member inline Create(a:^N, b:^N) =
    let ops = 
      { new IAddition< ^N > with
          member x.Add(a, b) = a + b }
    MyType< ^N >(a, b, ops)

现在您可以编写MyType<_>.Create(1, 2)并且整数的+操作将被自动捕获并存储在一个界面中(在其余代码中更容易使用)。

答案 1 :(得分:2)

MSDN上的

Statically Resolved Type Parameters可能会澄清对inline的需求。最重要的是你不能把约束放在界面上,而且它也不是精确的位置。 (你真的希望一个接口“泄漏”什么相当于一个实现细节吗?记住,这个约束只存在于编译时。)函数可能是inline,所以这有效:

type IMyInterface<'b> =
    abstract member myFunction : 'b -> 'b

// (^b : (static member (+) ...) constraint is inferred
let inline makeMyInterface() =
  { new IMyInterface<_> with
      member x.myFunction b = b + b }