Inside F#

Brian's thoughts on F# and .NET

F# async on the client side

Posted by Brian on March 27, 2010

Though the killer app for F# async is non-blocking I/O on the server side, F# asyncs are also useful and fun to use for client-side programming on the UI thread.  I’ve mentioned this before in the context of a large example, but I wanted to call out this detail in a separate blog post today.  I’ll show two small examples of F# async lets you do non-blocking loops on the UI thread.

Clock example

The first example is a clock.  Say I want to write a simple WinForms app that displays a digital clock that updates every second:

FSharpClock

There are a variety of ways you can “get off the UI thread” to do background work (e.g. BackgroundWorker) and get back to the UI thread (e.g. Control.Invoke, SynchronizationContext.Post).  When you don’t need to do any work, and just want to sleep or get called back in the future (as we’d need to for this ‘clock’ app), you could just use a Timer.  But who wants to fiddle with the details of all those cumbersome APIs?  In my head, I just want to do a simple loop:

    while true do
        sleep for 1 second
        display the new time on the clock

Simple, right?  The problem is, if you author a synchronous infinite loop on the UI thread like that, you end up with a blank UI that just has the ‘wait cursor’ (e.g. the hourglass or the spinny blue circle) forever.

Fortunately, F# offers “async”, so we can write the code just as I conceptualized it.  Here’s the entire program (add a reference to “System.Windows.Forms” to run it as an application):

    open System.Windows.Forms 
    let form = new Form(Visible = true)
    let textBox = new TextBox(ReadOnly = true, Text = "Hello world")
    form.Controls.Add(textBox)
    async {
        while true do
            do! Async.Sleep 1000
            textBox.Text <- System.DateTime.Now.ToString()
    } |> Async.StartImmediate         
    [<System.STAThread>]
    do Application.Run(form)

Right in the middle of the code you see my “while true” loop, just like I imagined it.  They key is that it’s inside an “async{…}” block, and when I sleep, I do an Async.Sleep with a “do!”.  Recall that F# asyncs enable you to “let go of the thread” for long-running async operations at “let!” and “do!” points; when the async operation finally completes, your code will pick back where it left off (via an implicit callback).  So this code “just works”.  The call to Async.StartImmediate starts running the async block on the current thread (the UI thread, in this example).

So there you have it – an infinite loop running on the UI thread without jamming up the UI – thanks to asynchronous non-blocking calls.

Text chasing the mouse

This is a cute little demo that is fun to play with.  The screenshot doesn’t quite do it justice:

FSharpReacts

so you should grab the code below, put it in an F# application (add the standard WPF references: PresentationCore, PresentationFramework, System.Xaml, UIAutomationTypes (if .NET 4.0), and WindowsBase) and run it right now.  The sentence “F# reacts to events!” will chase your mouse around the window in a manner that is surprisingly amusing.  (One of these days, I really need to learn enough Silverlight to host these cute UI apps directly in my blog; I’m such a slacker, I bet it’s easy to do.)

In F#, events like "MouseMove” are first class entities – I can pass them as arguments to functions, use them in pipelines, etc.  Furthermore, in F#, events are IObservables (which I’ve discussed previously).  So it’s trivial to write code to respond to these events using Observable functions (there’s no need for elaborate Visual Studio tooling to write “event handler callback method skeletons” for you, or whatnot).

If I imagine in my head how to write this app, the idea I have is: every time the mouse moves, I want to have the first letter of the sentence move to follow it, and then after a short delay have the second letter move to follow it, and then after a short delay the next letter, and so on.

Once again, I can encode this logic directly in F#.  The code is below, the first 20 lines of code just create a WPF Window containing a Canvas, and an array of TextBlocks (called “chars”) for each character in the sentence.

open System.Windows 
open System.Windows.Controls 
open System.Windows.Media 

type MyWindow() as this = 
    inherit Window()   

    let WIDTH = 20.0
    let canvas = new Canvas(Width=800.0, Height=400.0, Background = Brushes.White) 
    let chars = 
        " F# reacts to events!"
        |> Seq.map (fun c -> 
            new TextBlock(Width=WIDTH, Height=30.0, FontSize=20.0, Text=string c, 
                          Foreground=Brushes.Black, Background=Brushes.White))
        |> Seq.toArray 
    do
        this.Content <- canvas 
        this.Title <- "MyWPFWindow" 
        this.SizeToContent <- SizeToContent.WidthAndHeight  
        for tb in chars do                                     
            canvas.Children.Add(tb) |> ignore 

        this.MouseMove 
        |> Observable.map (fun ea -> ea.GetPosition(this))
        //|> Observable.filter (fun p -> p.X < 300.0)
        |> Observable.add (fun p -> 
            async {
                for i in 0..chars.Length-1 do
                    do! Async.Sleep(90)
                    Canvas.SetTop(chars.[i], p.Y)
                    Canvas.SetLeft(chars.[i], p.X + float i*WIDTH)
            } |> Async.StartImmediate
        )

[<System.STAThread()>] 
do  
    let app =  new Application() 
    app.Run(new MyWindow()) |> ignore 

Look at the bit starting with “this.MouseMove”.  I pipe the MouseMove event into a call to Observable.map to project out just the position (x-y coordinate) of the MouseMove event relative to this window.  Now I have an IObservable<Point>, so I pipe that result into Observable.add to add a handler.  In this handler, I just run the async loop I imagined in my head: for each character in the sentence, I have a short delay, and then move that character to chase the mouse (by updating its top & left positions relative to the Canvas, to match the mouse-move point “p”).

Since this little loop-over-each-character runs asynchronously, we don’t jam up the UI; while the latter portion of the sentence is still chasing the old mouse position, we can still react to newer mouse moves and have the earlier characters chasing the updated position.  You end up with lots of interleaved loops, all running asynchronously on the UI thread, and it “just works” and you get a cute, fluid animation.

There’s a line commented out; if you uncomment it, you can probably guess what happens.  The line filters only those locations whose X coordinate is less than 300, so if you run the app with that line uncommented, then the letters only chase the mouse while it’s on the left portion of the Canvas, but quit chasing when the mouse goes too far to the right. 

By the way, this demo is one I stole from a recent presentation I did with Matthew Podwysocki at TechReady.  We, in turn, stole the idea from the “time flies like an arrow” sample that Erik Meijer and the Rx team use to show off Reactive Extensions. :) Such a cute little demo!

Summary

These examples demonstrate how easy it is to use F# asyncs to create some simple UI effects.  Normally you can’t write a long-running loop on the UI thread, as it will make the UI non-responsive (no repainting, no responding to the mouse, etc.).  But with F# async, we can write loops with non-blocking calls (like “Async.Sleep”), so that we can write for-loops or while-loops that run for many seconds (or even forever) without blocking the UI.

(The real killer app for F# asyncs is server-side, and I hope to blog about that sometime soon.)

About these ads

One Response to “F# async on the client side”

  1. Hi Brian. Time does fly …
    “… (The real killer app for F# asyncs is server-side …) ”

    Is F# Async a killer app on the server-side?
    I’ve seen a number of articles, and understand how it fits.
    Has C++,# adopted Async?

    What is the killer app for F# Async Rx?

    Refolding Planar Polygons does computational geometry, but needs polygon input and display animation. F# is good for the CompGeom, not so much UX input (but I’m hoping to figure out Async, and Async Rx for UX), and WPF is good for the display animation. With all the new surfaces, cloud, cluster, client, etc. I’m having difficulty keeping up with the road maps.

    I like the cute little demos. More please.

    Best in ’12!
    PLH OOE Art

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 30 other followers

%d bloggers like this: