views:

105

answers:

3

Suppose I have a family of types which support a given member function, like the Property member below:

type FooA = {...} with

    member this.Property = ...

type FooB = {...} with

    member this.Property = ...

Suppose the member Property returns an integer for each of the the above types. Now, I wan to write a generic function that could do the following:

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

I am used to write the following in C++:

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

How would be an equivalent implementation in F#?

+5  A: 

It is possible to do this in F#, but it isn't idiomatic:

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

In general, .NET generics are very different from C++ templates, so it's probably worth learning about their differences first before trying to emulate C++ in F#. The general .NET philosophy is that operations are defined by nominal types rather than structural types. In your example you could define an interface exposing a Property property, which your two classes implement, and then your sum function would take instances of that interface.

See http://msdn.microsoft.com/en-us/library/dd233215.aspx for more information on F# generics; for a brief comparison of .NET generics versus C++ templates see http://blogs.msdn.com/b/branbray/archive/2003/11/19/51023.aspx (or really anything that comes up when you Google ".NET generics C++ templates").

kvb
It may not be idiomatic, but it can be damn handy. For example, I use this to get easy access to all those convenient explicit conversion operators defined on XElement and XAttribute: `let inline xval (x : ^a when ^a :> XObject) : ^b = ((^a or ^b) : (static member op_Explicit : ^a -> ^b) x)` Then I can just do something like this: `let foo:int = xval myElement`
Joel Mueller
Thanks. Though more verbose than the C++ version, it works. Where can I learn more about generics in F#?
Allan
@Allan - I've edited my answer to add a few links to further reading.
kvb
+2  A: 

I think that the clean approach in this case (even in F#) is to use basic object-oriented programming and define an interface with the members you require (e.g. Property):

type IHasProperty =
  abstract Property : int

Note that F# infers that we're declaring an interface, because it has only abstract members and no constructor. Then you can implement the interface in your types:

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

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

It is worth pointing out that any F# types (including records, discriminated unions and of course classes) can implement interfaces. Now, the generic function can just take two arguments of the type IHasProperty:

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

You don't even need generics in this case, but there are several tricks that generics can do with interfaces too - you can require the type parameter T to implement some specified interface, but this is not needed here, because F# will automatically convert arguments from types like FooA to the interface when you write sum f1 f2.

The use of interfaces and types that are immutable makes often a very good sense in F#. There may be a "more functional" solution to your problem, but that would require more context.

Tomas Petricek
What if I can't change the code? That is, the types already implement the member Property without using interfaces. It is not my case; just curious.
Allan
@Allan: In that case, the only option would proably be to use static member constraints as suggested by kvb.
Tomas Petricek
For example, in a linear algebra library, I could have many kinds of matrices (e.g. Dense, Sparse, Band, etc.). In C++ I would write arithmetic operations using template in such way that could work for any of these matrices. For an addition function, for example, I would require that the type provides the member operator(i,j) to access the matrix entry i,j.
Allan
+2  A: 

C++ templates roughly use a kind of 'structural static subtyping' relation (somewhat like 'duck typing'), whereas .NET generics use nominal subtyping. As @kvb says, you can emulate the C++ stuff with inline and static member constraints in F#, but be very wary of doing this. There are a few other ways to express a similar relation; @Tomas shows one (OO subtyping), and another would be to pass a method dictionary (that knows how to project out .Property from a fixed set of types). (If this were Haskell, you could use typeclasses.)

Brian