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
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: