F Sharp Programming/Events

Events allow objects to communicate with one another through a kind of synchronous message passing. Events are simply hooks to other functions: objects register callback functions to an event, and these callbacks will be executed when (and if) the event is triggered by some object.

For example, let's say we have a clickable button which exposed an event called. We can register a block of code, something like, to the button's click event. When the click event is triggered, it will execute the block of code we've registered. If we wanted to, we could register an indefinite number of callback functions to the click event—the button doesn't care what code is trigged by the callbacks or how many callback functions are registered to its click event, it blindly executes whatever functions are hooked to its click event.

Event-driven programming is natural in GUI code, as GUIs tend to consist of controls which react and respond to user input. Events are, of course, useful in non-GUI applications as well. For example, if we have an object with mutable properties, we may want to notify another object when those properties change.

Defining Events
Events are created and used through F#'s Event class. To create an event, use the  constructor as follows:

To allow listeners to hook onto our event, we need to expose the  field as a public member using the event's   property:

Now, any object can listen to the changes on the person method. By convention and Microsoft's recommendation, events are usually named  or , as well as adding tenses like   and   to indicate post- and pre-events.

Adding Callbacks to Event Handlers
Its very easy to add callbacks to event handlers. Each event handler has the type  which exposes several methods:

 
 * Connect a listener function to the event. The listener will be invoked when the event is fired.

 
 * Connect a handler delegate object to the event. A handler can be later removed using RemoveHandler. The listener will be invoked when the event is fired.

 
 * Remove a listener delegate from an event listener store.

Here's an example program:

This program outputs the following:

Event handling is easy -- Name changed! New name: Joe It handily decouples objects from one another -- Name changed! New name: Moe It's also causes programs behave non-deterministically. -- Name changed! New name: Bo -- Another handler attached to NameChanged! The function NameChanged is invoked effortlessly.


 * Note: When multiple callbacks are connected to a single event, they are executed in the order they are added. However, in practice, you should not write code with the expectation that events will trigger in a particular order, as doing so can introduce complex dependencies between functions. Event-driven programming is often non-deterministic and fundamentally stateful, which can occasionally be at odds with the spirit of functional programming. Its best to write callback functions which do not modify state, and do not depend on the invocation of any prior events.

Adding and Removing Event Handlers
The code above demonstrates how to use the  method. However, occasionally we need to remove callbacks. To do so, we need to work with the  and   methods, as well as .NET's built-in System.Delegate type.

The function  has the type , where   inherits from. We can create an instance of  as follows:

This program outputs the following:

Event handling is easy -- Name changed! New name: Joe It handily decouples objects from one another -- Name changed! New name: Moe It's also causes programs behave non-deterministically. -- Another handler attached to NameChanged! The function NameChanged is invoked effortlessly.

Defining New Delegate Types
F#'s event handling model is a little different from the rest of .NET. If we want to expose F# events to different languages like C# or VB.NET, we can define a custom delegate type which compiles to a .NET delegate using the  keyword, for example:

The convention  corresponds to the .NET naming guidelines which recommend that all events have the type.

Use existing .NET WPF Event and Delegate Types
Try using existing .NET WPF Event and Delegate, example, ClickEvent and RoutedEventHandler. Create F# Windows Application .NET project with referring these libraries (PresentationCore PresentationFramework System.Xaml WindowsBase). The program will display a button in a window. Clicking the button will display the button's content as string.

|> ignore                      // ignore the return because f-signature requires: obj->RoutedEventArgs->unit let d = new RoutedEventHandler(f)      // (#2) d will have type-RoutedEventHandler, //     RoutedEventHandler is a kind of delegate to handle Button.ClickEvent. //     The f must have signature governed by RoutedEventHandler. b.AddHandler(Button.ClickEvent,d)      // (#1) attach a RountedEventHandler-d for Button.ClickEvent let w = new Window(Visibility=Visibility.Visible,Content=b) // create a window-w have a Button-b // which will show the content of b when clicked (new Application).Run(w)     // create new Application running the Window-w. (#1) To attach a handler to a control for an event:  (#2) Create a delegate/handler using a function:  (#3) Create a function with specific signature defined by the delegate:  b is the control. AddHandler is attach. Button.ClickEvent is the event. d is delegate/handler. It is a layer to make sure the signature is correct f is the real function/program provided to the delegate. <li>Rule#1: b must have this event Button.ClickEvent: b is type-Button-object. ClickEvent is a static property of type-ButtonBase which is inherited by type-Button. So Button-type will also have this static property ClickEvent. <li>Rule#2: d must be the handler of ClickEvent: ClickEvent is type-RoutedEvent. RoutedEvent's handler is RoutedEventHandler, just adding Handler at end. RoutedEventHandler is a defined delegate in .NET library. To create d, just let d = new RoutedEventHandler(f), where f is function.</li> <li>Rule#3: f must have signature obeying delegate-d's definition: Check .NET library, RoutedEventHandler is a delegate of C#-signature:. So f must have same signature. Present the signature in F# is </li>

Passing State To Callbacks
Events can pass state to callbacks with minimal effort. Here is a simple program which reads a file in blocks of characters:

This program has the following types:

Since our event has the type, we can pass   data as state to listening callbacks. This program outputs the following:

0 percent done... 4 percent done... 9 percent done... 14 percent done... 19 percent done... 24 percent done... 29 percent done... 34 percent done... 39 percent done... 44 percent done... 49 percent done... 53 percent done... 58 percent done... 63 percent done... 68 percent done... 73 percent done... 78 percent done... 83 percent done... 88 percent done... 93 percent done... 98 percent done... 100 percent done... In computer programming, event-driven programming or event-based programming is a programming paradig[...]

Retrieving State from Callers
A common idiom in event-driven programming is pre- and post-event handling, as well as the ability to cancel events. Cancellation requires two-way communication between an event handler and a listener, which we can easily accomplish through the use of ref cells or mutable members:

This program has the following types:

This program outputs the following: Attempting to change name to 'Joe' -- Name allowed -- Name changed to Joe Attempting to change name to 'Moe' -- Name allowed -- Name changed to Moe Attempting to change name to 'Jon' -- No Jon's allowed! Attempting to change name to 'Thor' -- Name allowed -- Name changed to Thor

If we need to pass a significant amount of state to listeners, then its recommended to wrap the state in an object as follows:

By convention, custom event parameters should inherit from, and should have the suffix.

Using the Event Module
F# allows users to pass event handlers around as first-class values in fundamentally the same way as all other functions. The Event module has a variety of functions for working with event handlers:

 
 * Return a new event which fires on a selection of messages from the original event. The selection function takes an original message to an optional new message.

 
 * Return a new event that listens to the original event and triggers the resulting event only when the argument to the event passes the given function.

 
 * Run the given function each time the given event is triggered.

 
 * Return a new event which fires on a selection of messages from the original event. The selection function takes an original message to an optional new message.

 
 * Fire the output event when either of the input events fire.

 
 * Return a new event that triggers on the second and subsequent triggerings of the input event. The Nth triggering of the input event passes the arguments from the N-1th and Nth triggering as a pair. The argument passed to the N-1th triggering is held in hidden internal state until the Nth triggering occurs. You should ensure that the contents of the values being sent down the event are not mutable. Note that many EventArgs types are mutable, e.g. MouseEventArgs, and each firing of an event using this argument type may reuse the same physical argument object with different values. In this case you should extract the necessary information from the argument before using this combinator.

 
 * Return a new event that listens to the original event and triggers the first resulting event if the application of the predicate to the event arguments returned true, and the second event if it returned false.

 
 * Return a new event consisting of the results of applying the given accumulating function to successive values triggered on the input event. An item of internal state records the current value of the state parameter. The internal state is not locked during the execution of the accumulation function, so care should be taken that the input IEvent not triggered by multiple threads simultaneously.

 
 * Return a new event that listens to the original event and triggers the first resulting event if the application of the function to the event arguments returned a Choice2Of1, and the second event if it returns a Choice2Of2.

Take the following snippet:

We can rewrite this in a more functional style as follows: