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.