You are previewing Programming F#.

Programming F#

Cover of Programming F# by Chris Smith Published by O'Reilly Media, Inc.
O'Reilly logo

Delegates and Events

So far the classes and types we have created have been useful, but they needed to be acted upon in order for something to happen. However, it would be beneficial if types could be proactive instead of reactive. That is, you want types that act upon others, and not the other way around.

Consider Example 8-8, which defines a type CoffeeCup that lets interested parties know when the cup is empty. This is helpful in case you want to automatically get a refill and avoid missing out on a tasty cup of joe. This is done by keeping track of a list of functions to call when the cup is eventually empty.

Example 8-8. Creating proactive types

open System.Collections.Generic

[<Measure>]
type ml

type CoffeeCup(amount : float<ml>) =
    let mutable m_amountLeft = amount
    let mutable m_interestedParties = List<(CoffeeCup -> unit)>()

    member this.Drink(amount) =
        printfn "Drinking %.1f..." (float amount)
        m_amountLeft <- min (m_amountLeft - amount) 0.0<ml>
        if m_amountLeft <= 0.0<ml> then
            this.LetPeopleKnowI'mEmpty()

    member this.Refil(amountAdded) =
        printfn "Coffee Cup refilled with %.1f" (float amountAdded)
        m_amountLeft <- m_amountLeft + amountAdded

    member this.WhenYou'reEmptyCall(func) =
        m_interestedParties.Add(func)

    member private this.LetPeopleKnowI'mEmpty() =
        printfn "Uh oh, I'm empty! Letting people know..."
        for interestedParty in m_interestedParties do
            interestedParty(this)

When used in an FSI session, the following transpires after a refreshing cup of coffee has been emptied:

> let ...

The best content for your career. Discover unlimited learning on demand for around $1/day.