About the book
This is a sample chapter of the book
Real-World Functional Programming. It has been published with the exclusive
permission of Manning.
Written by: Tomas Petricek with Jon Skeet
Pages: 560
Publisher: Manning
ISBN: 9781933988924
Get 30% discount
DotNetSlacker readers can get 30% off the full print book or ebook at
www.manning.com using the promo code dns30 at checkout.
Introduction
When designing applications that don't react to external events, you have lots of control flow constructs available, such as if
-then
-else
expressions, for
loops and while
loops in imperative languages, or recursion and higher-order functions in functional languages. Constructs like this make it easy to describe what the application does. The control flow is clearly visible in the source code, so drawing a flowchart to describe it is straightforward.
Understanding reactive applications is much more difficult. A typical C# application or GUI control that needs to react to multiple events usually involves mutable state. When an event occurs, it updates the state and may run more code in response to the event, depending on the current state. This architecture makes it quite difficult to understand the potential states of the application and the transitions between them. Using asynchronous workflows, we can write the code in a way that makes the control flow of the application visible even for reactive applications.
Waiting for events asynchronously
The reason why we can't use standard control flow constructs to drive reactive applications is that we don't have any way of waiting for an event to occur. Writing a function that runs in a loop and checks whether an event has occurred is not only difficult to implement, it's also very bad practice: it would block the executing thread. As you learned in chapter 13, asynchronous workflows allow us to write code that looks sequential, but which can include waiting for external events (such as the completion of an asynchronous I/O operation) but is executed asynchronously without blocking the thread.
So far, we've seen only asynchronous methods that perform I/O operations, but we can also define a primitive that stops the asynchronous workflow and resumes it when the specified event occurs. The primitive, called AwaitObservable
, is available in the online source code for the book as an extension for the Async
type. Let's start by looking at its type signature:
The type shows us that the function is quite simple. It takes event as an argument and returns a value that we can use inside an asynchronous workflow using the let
! keyword. One important difference between events and Async<'T>
values is that an asynchronous workflow can be executed at most once, while events can be triggered multiple times. This means that the AwaitObservable
function has to wait only for the first occurrence of the event and then resumes the asynchronous workflow. Let's take a look at how we can use AwaitObservable
in a GUI application.
Counting mouse clicks
We'll start by implementing an example similar to the counter increment/decrement application we used to demonstrate the Observable
module higher-order functions. This will be simpler: it counts the number of clicks that take place and displays the count on a label. This behavior could be implemented using Observable.scan
and the source code would be shorter, but as we'll see later AwaitObservable
is a far more powerful construct. The following listing shows to write event handling code using asynchronous workflows.
Listing 1: Counting clicks using asynchronous workflows
The essential part of the application that implements the counting is a single recursive function that's implemented as an asynchronous workflow. The function appears to create an infinite loop, which sounds suspicious to start with. The construct is completely valid, because it starts by waiting for a MouseDown
event. This is done asynchronously, which means that the workflow will install the event handler and the rest will only be executed when the user clicks the label. Once the event occurs, we update the text and loop with the incremented counter.
Earlier we mentioned that the AwaitObservable
primitive waits for the first occurrence of the event, because asynchronous workflows can yield only a single value. As you can see in this example, if we want to handle every occurrence of the event, we can simply use a recursive loop to wait for the next occurrence. Using recursion also allows us to store the current state in the function parameters. In fact, this technique for expressing computations is similar to the primitive recursive functions we saw earlier in the book.
When working with Windows Form controls, we're required to access them only from the GUI thread, which is the main thread of the application. When you try to use property from other threads, the behavior is undefined and the application could crash. This means that we need to make sure that the asynchronous workflow will be executed only on the GUI thread. So far, we didn't really care where the workflow executes, but F# libraries provide a mechanism to control that.
First, most of the asynchronous operations return to the calling thread after completion. This means that when you invoke an operation such as AsyncGetResponse
, AsyncRead
, or Async.Sleep
, the operation will release the calling thread and start executing in background. When it completes (usually on some background thread), it will use the .NET SynchronizationContext
class to return to the thread where it was started.
When we start the workflow on the GUI thread, it will continue running on the GUI thread even if the workflow includes some operations that involve background threads. Thanks to this behavior, we can safely access Windows Forms controls from any part of the workflow. The only remaining question is how we can start workflow on the GUI thread. In listing 16.9, we use the Async.StartImmediate
primitive (#4), which runs the workflow on the current thread. When the application starts, the current thread will be the main GUI thread.
The following figure shows what happens when we use the StartImmediate
primitive to run a workflow that contains a call to AsyncGetResponse
. The most important fact is that when we run an asynchronous operation (using the let!
primitive), the GUI thread is free to perform other work. When the workflow running on a GUI thread spends most of the time waiting for completion of an asynchronous operation, the application won't become unresponsive.

StartImmediate
starts the workflow on a GUI thread. The AsyncGetResponse
operation runs in the background, while the GUI thread can perform other work. When the background operation completes, the workflow returns to the GUI thread.
As we said earlier, we could easily have implemented this example using Observable.scan
; let's look at a slightly more complicated problem.
Limiting the speed of clicks
Let's say that we'd like to limit the rate of clicks. We want the count to stay the same at least for one second after it gets incremented by the user clicking on the label. One way for implementing this is to add another parameter to the loop
function of type DateTime
that will store the last time of a successful click. When the event occurs inside the loop, we could then check the difference from the current time and the last time and increase the count only when the difference is larger than the limit.
There's a much simpler way of achieving this. In chapter 13 we discussed the Thread.AsyncSleep
method, which allows us to stop the workflow for a specified time. If we use it somewhere in the loop function, it will sleep for one second before reacting to the next event, which is exactly what we wanted. All we have to do is to add the following single line before the line that last line that runs the recursion:
This is already something that would be quite difficult to do using the functions from the Observable
module. If you're curious, you can find the solution using Observable
functions in the source code at this book's website; it's about 8 lines long and a bit tricky to understand. The control flow of this example was still pretty simple. In the next section, we'll explore a more sophisticated example that better demonstrates the capabilities of using asynchronous workflows for GUI programming.
Drawing rectangles
One problem that's surprisingly difficult to solve in a functional way is drawing graphical objects on a Windows Forms control. Suppose we want to draw a rectangle so that the user starts by pressing the mouse button in one of the corners, moves the cursor to the opposite corner, then releases the button. While moving the cursor with the button pressed, the application should draw the current shape of the rectangle, and when the button is released, it should be finally applied to a bitmap or stored in the list of vector shapes.
A typical imperative implementation would use a mutable flag specifying whether we're currently drawing and a mutable variable to store the last location where the user pressed the mouse button. Then we'd handle MouseDown
and MouseMove events and modify the state appropriately when one of them fired. We can check whether the drawing is finished in the handler for the MouseMove
event, because it also carries information about the state of mouse buttons. Alternatively, we could use the MouseUp
button, but the first version will be easier to start with. If we think of the control flow of the application, we can see that it's quite simple. Here is a flowchart that demonstrates this logic:

When the application is Waiting
, we can press the button to start Drawing
. In this state, we can either continue Drawing
by moving the mouse or complete the task and change the state of the application back to Waiting
by releasing the button.
We're almost ready to convert this state machine into an F# program using asynchronous workflows, but first we need a form to draw on and a utility function to help us with the basic task of drawing a rectangle.
Implementing program fundamentals
We'll improve this application later, but let's start with an empty form on which we can draw rectangles. The following listing shows the code required to create the form and a function, drawRectangle
, that draws a rectangle on the form using the specified color and two of any corner points of the rectangle.
Listing 2: Creating a user interface and drawing utility
The code is very straightforward. The function drawRectangle
takes all its arguments as a tuple, so it can be used in a way that's consistent with calling .NET methods. In addition, its second and third parameters are nested tuples that represent the X and Y coordinates of the corners of the rectangle. This makes the rest of the code easier.
Implementing the state machine
Now that we have all the basics of the application, we can implement our user interaction. We're going to follow the state machine described in the figure shown earlier, with two states (Waiting
and Drawing
) that have various transitions between them. Asynchronous workflows allow us to translate this directly, representing each state with a single function. The transitions can be encoded as function calls or by returning a value from a function.
For our example this means that we'll have two functions called drawingLoop
and waitingLoop
. The first of these also needs to remember some state, which we represent using the function's parameters.
Listing 3: Workflow for drawing rectangles
The most direct way to encode the state machine would be to use recursive calls between the two functions using the return
! keyword. Listing 16.11 makes a minor change to this, to aid readability. The waitingLoop
function contains an infinite while
loop that waits until the user clicks the left button, then transfers control to the drawingLoop
function. When drawingLoop
completes, it returns the end position of the rectangle and transfers the control back to waitingLoop
. We can then print the information about the drawn rectangle and wait for another MouseDown
event.
The function that runs while the user is drawing a rectangle is looping using recursive calls, because it needs to keep some state.It starts by waiting for the MouseMove
event, which is also triggered when the button is released. It then tests whether the button is currently pressed; if that's the case, it refreshes the view of the form. This transition corresponds to the arc looping in the Drawing
state. When the button is released, it returns the last location as a result, which is the transition back to the Waiting
state.
That's almost everything we need to run the application. All that remains is to start the asynchronous workflow that handles drawing of rectangles and run the application. We'll use the Async.StartImmediately
primitive to start the workflow on the GUI thread:
In this simple application, we need only a single asynchronous workflow that handles all the interaction with the application, but multiple workflows can be combined easily. If we wanted to allow polygons to be drawn using the right mouse button, we could implement that without making any changes to the existing code. We'd simply create another workflow for drawing polygons and start it independently using Async.StartImmediately
. This way of writing the UI code gives us a modular way of splitting complex interactions into separate processes.
Running workflows on the GUI threadThe application we just implemented consists of a single running process, but it's important to realize that a process in the sense we're using here doesn't correspond to a thread. Even if we had multiple processes waiting for GUI events, the application would still be single-threaded.
When we run the Async.StartImmediately
method in the earlier example, it starts running the workflow and doesn't complete until the workflow reaches a point where it waits for a completion of an asynchronous operation (such as waiting for an event). Both Async.Sleep
and Async.AwaitObservable
, which we've used so far, return to the caller thread after completion, so the workflow will continue running exclusively on the GUI thread.
Even if we add multiple processes that wait for UI events, this technique doesn't introduce any parallelism. All the code runs on the GUI thread, and if a single event causes state transition in multiple processes, the bits of workflows are executed sequentially. Using asynchronous workflows to write UIs gives us an easier way to write our single-threaded GUI processing.
Later on, we'll see a technique that allows us to integrate this form of GUI processing with other processes that can potentially run in parallel. However, code for user interface interaction like this should be simple and shouldn't perform any complicated computations, so there's no need for parallelism. Even when we need to perform some time-consuming computations for the GUI, it's still a good idea to move all this work to a background worker thread.
The code we've written so far isn't a drawing application, because it doesn't store the rectangles we've drawn. Once a rectangle has been completed by the user releasing the button, it prints information to the console and forgets the rectangle. We could store a list of rectangles as a parameter of the waitingLoop
function (if we made it a recursive function), but that would cause other problems. The list would be private to the drawing loop, so it couldn't be accessed from other parts of the application. We need a different approach to handle global state that's used by the whole application.
Summary
In this article, we've explored a powerful programming idiom based on F# asynchronous workflows. We've seen that we don't have to handle all events by attaching a handler to the event and then modifying global state of the application (or a control) when the event occurs. Instead, we can use asynchronous workflows and write code that waits for events, resumes performs some action and then waits for another event. This programming model is very comfortable, because we can use high-level language constructs such as (imperative) loops or (functional) recursion to express control flow logic such as drag&drop algorithm.
Get 30% discount
DotNetSlacker readers can get 30% off the full print book or ebook at
www.manning.com using the promo code dns30 at checkout.
About Manning Publications
 |
Manning Publication publishes computer books for professionals--programmers, system administrators, designers, architects, managers and others. Our focus is on computing titles at professional levels. We care about the quality of our books. We work with our authors to coax out of them the best writi...
This author has published 33 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Adding users to a TFS project when youre not on the domain
read more
Creating a Filtering User Interface With jQuery In a Web Forms Application: Part 1
read more
Unit Testing - Do Repeat Yourself
read more
Update to Logging in to DotNetNuke from a Silverlight Application with RIA Authentication
read more
Session State Not Working? Check Your Web Garden!
read more
Telerik Announces Support for Microsoft Silverlight 3
read more
Principle of Least Surprise
read more
Telerik Introduces Free Web Testing Framework for ASP.NET AJAX and Silverlight
read more
Examining ASP.NET's Membership, Roles, and Profile - Part 15
read more
Customizing ASP.NET's CreateUserWizard Control To Display a Fixed Set of Security Questions
read more
|
|
Please login to rate or to leave a comment.