Object expression

Introduction

Expression used to implement an abstract type on the fly

let makeResource (resourceName: string) =
    printfn $"create {resourceName}"
    { new System.IDisposable with
        member _.Dispose() =
            printfn $"dispose {resourceName}" }

☝ Notice the signature of makeResource: string -> System.IDisposable. → The backing class is hidden/transparent at the F♯ level. → Advantage: Upcasting is not required, compared to interface implementation in a type.

Interface singleton

We can implement a Singleton using an object expression:

[<Interface>]
type IConsole =
    abstract ReadLine : unit -> string
    abstract WriteLine : string -> unit

let console =
    { new IConsole with
        member this.ReadLine () = Console.ReadLine ()
        member this.WriteLine line = printfn "%s" line }

Implementing several interfaces

Implementing several interfaces in an object expression is possible, but only the first interface is visible. To use the members of the other interfaces, we need to perform a downcast, which is unsafe by nature❗

let makeDelimiter (delim1: string, delim2: string, value: string) =
    { new System.IFormattable with
        member _.ToString(format: string, _: System.IFormatProvider) =
            if format = "D" then
                delim1 + value + delim2
            else
                value
      interface System.IComparable with
        member _.CompareTo(_) = -1 }

let o = makeDelimiter("<", ">", "abc")
// val o : System.IFormattable
let s = o.ToString("D", System.Globalization.CultureInfo.CurrentCulture)
// val s : string = "<abc>"
let i = (d :?> System.IComparable).CompareTo("cde")  // ❗ Unsafe
// val i : int = -1

👉 Prefer to split the object in 2.

Last updated

Was this helpful?