Interface vs record of functions

Conceptually, records of functions are very similar to interfaces and most of the time, you could use both of them to solve a given problem (so this is a great & valid question).

If you look just at the technical aspects, then the biggest difference is that interfaces can have generic methods. This is something that cannot be done using records - for example, there is no way to define a simple record of functions that would correspond to the following:

type IFoo<'T> =
   abstract Bar<'R> : 'R -> 'T

However, in practice, I think the more important differences are related to interoperability and design:

  • Interfaces interoperate nicely with C# and other .NET components
    (Constructing an F# record of functions from C# would be very hard and ugly)
  • Interfaces express a different intention - they are interface that can be satisfied and implemented.
    On the other hand, records are collections of functions that are created.
  • Records are nice if you need to use the { oldValue with NewFunction = newFunction } construction to replace one function.

In general, I mostly use records when I need to keep some state during e.g. recursive processing of some data structure and I need the with construct. For public API, I believe that using interfaces and simple classes is better abstraction than using records.


There are two very important differences between records and objects/interfaces. One is that records have no "self/this" concept. The other is that records support with operations. Combined, it makes records of functions awkward for implementing a service reference.

For instance if you want to create a PrintX service, using records a first attempt might look like this.

let createService x = { X = x; PrintX = fun () -> printfn x }
let myService = createService 1
let twoService = { myService with X = 2 }
twoService.PrintX()

It prints 1, not 2. Sure you can say “well don’t do this”. And sure you can "fix" it by closing over a mutable x and providing a SetX(x) function in your record. But the fact that the construct allows the user to do something nonsensical indicates maybe the construct itself isn’t quite right for representing your intention. It's really a square hole for a round peg.

So the recognition I’ve come to is that, for things that are nothing more than the sum of their parts, including bags of independent functions, records are perfect. But for things that together form a coherent whole, objects/interfaces are really more appropriate.

Tags:

F#