Interfaces in F#

Interfaces in F# are typically implemented using object expressions or interfaces. For this example, we’ll use F# interfaces and classes.

open System

// Here's a basic interface for geometric shapes.
type IGeometry =
    abstract member Area : unit -> float
    abstract member Perim : unit -> float

// For our example we'll implement this interface on
// Rectangle and Circle types.
type Rectangle(width: float, height: float) =
    interface IGeometry with
        member this.Area() = width * height
        member this.Perim() = 2.0 * width + 2.0 * height
    
    override this.ToString() = sprintf "{%.1f %.1f}" width height

type Circle(radius: float) =
    interface IGeometry with
        member this.Area() = Math.PI * radius * radius
        member this.Perim() = 2.0 * Math.PI * radius
    
    override this.ToString() = sprintf "{%.1f}" radius

// To implement an interface in F#, we define the methods
// inside the interface implementation block.

// Here's a generic measure function taking advantage of
// interfaces to work on any IGeometry.
let measure (g: IGeometry) =
    printfn "%O" g
    printfn "%f" (g.Area())
    printfn "%f" (g.Perim())

[<EntryPoint>]
let main argv =
    let r = Rectangle(3.0, 4.0)
    let c = Circle(5.0)

    // The Rectangle and Circle types both
    // implement the IGeometry interface so we can use
    // instances of these types as arguments to measure.
    measure r
    measure c
    0

To run the program, save it as Interfaces.fs and use the F# compiler:

$ dotnet fsi Interfaces.fs
{3.0 4.0}
12.000000
14.000000
{5.0}
78.539816
31.415927

In this F# version:

  1. We define an IGeometry interface with Area and Perim methods.
  2. We create Rectangle and Circle classes that implement the IGeometry interface.
  3. The measure function takes an IGeometry parameter, allowing it to work with any type that implements this interface.
  4. In the main function, we create instances of Rectangle and Circle and pass them to the measure function.

F# uses a different syntax for defining and implementing interfaces, but the core concept remains the same: interfaces allow us to define a contract that multiple types can adhere to, enabling polymorphic behavior.