F#通用编程 - 使用成员

时间:2010-09-01 23:07:28

标签: generics f# constraints

假设我有一系列支持给定成员函数的类型,例如下面的Property成员:

type FooA = {...} with

    member this.Property = ...

type FooB = {...} with

    member this.Property = ...

假设成员Property为上述每种类型返回一个整数。现在,我想编写一个可以执行以下操作的通用函数:

let sum (a: 'T) (b: 'U) = a.Property + b.Property

我习惯用C ++编写以下内容:

template<typename T, typename U>
int sum(T a, U b)
{
    return a.Property + b.Property;
}

如何在F#中实现等效的实现?

3 个答案:

答案 0 :(得分:7)

可以在F#中执行此操作,但这不是惯用语:

let inline sum a b = (^T : (member Property : int) a) + (^U : (member Property : int) b)

一般来说,.NET泛型与C ++模板非常不同,因此在尝试使用F#模拟C ++之前,首先要了解它们之间的差异可能是值得的。一般的.NET哲学是操作由名义类型而不是结构类型定义。在您的示例中,您可以定义一个公开Property属性的接口,您的两个类实现该属性,然后您的sum函数将接受该接口的实例。

有关F#generics的更多信息,请参阅http://msdn.microsoft.com/en-us/library/dd233215.aspx;有关.NET泛型与C ++模板的简要比较,请参阅http://blogs.msdn.com/b/branbray/archive/2003/11/19/51023.aspx(或者当您使用Google“.NET泛型C ++模板”时出现的任何内容)。

答案 1 :(得分:2)

我认为在这种情况下(即使在F#中)干净的方法是使用基本的面向对象编程并使用您需要的成员定义接口(例如Property):

type IHasProperty =
  abstract Property : int

请注意,F#推断我们正在声明一个接口,因为它只有抽象成员而没有构造函数。然后,您可以在类型中实现接口:

type FooA = {...} 
  interface IHasProperty with
    member this.Property = ... 

type FooB = {...} 
  interface IHasProperty with
    member this.Property = ... 

值得指出的是,任何F#类型(包括记录,有区别的联合,当然还有类)都可以实现接口。现在,泛型函数只能采用IHasProperty类型的两个参数:

let sum (a: IHasProperty) (b: IHasProperty) = a.Property + b.Property 

在这种情况下你甚至不需要泛型,但是泛型也可以使用接口做一些技巧 - 你可以要求类型参数T来实现一些指定的接口,但这里不需要,因为当你编写FooA时,F#会自动将sum f1 f2等类型的参数转换为接口。

使用不可变的接口和类型通常在F#中非常有意义。您的问题可能有一个“更实用”的解决方案,但这需要更多的上下文。

答案 2 :(得分:2)

C ++模板大致使用一种“结构静态子类型”关系(有点像'鸭子打字'),而.NET泛型使用名义子类型。正如@kvb所说,您可以使用inline和F#中的静态成员约束来模拟C ++内容,但要非常谨慎。还有一些其他方式来表达类似的关系; @Tomas显示一个(OO子类型),另一个是传递一个方法字典(它知道如何从一组固定的类型中突出.Property)。 (如果这是Haskell,你可以使用类型类。)