Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Time Flies Like An Arrow in F# and the Reactive Extensions for .NET

In the past week, I had the pleasure of speaking on Reactive Programming in F# with Brian McNamara at a conference out in Seattle.  The point of this talk was to cover the what and why of using F# in reactive programming on both the client and the server and showed quite a few examples.  One of the samples Brian alluded to, the “Time Flies Like An Arrow” example, which is an example of having a stream of text, each character delayed behind the previous, follow the mouse around the screen.  Brian’s version used a combination of both the asynchronous workflows which are a standard part of F#, as well as first class events/observables.  For my version, I’m going to strictly use F# first class events and the integration with the Reactive Extensions for .NET to show you how it can be done.

Implementing Using F#

Let’s take a look at what would be required for us to make the Time Flies Like an Arrow example work in F# using first class events and the Reactive Extensions for .NET on top of WPF.  I’ll write it in a pseudo-code to give you an idea of how it should be done.

text = "F# Reacts to First Class Events!"

for character in text

  textbox = TextBox with text = character
  Add textbox to canvas
  
  MouseMove
  - Get Position relative to canvas
  - Delay by character position * 100
  - Observe on the dispatcher thread
  - Set the textbox top to the y position of the mouse
  - Set the textbox left to x position of the mouse
    plus character position * 10

Luckily, using F#, our code shouldn’t be much more complicated than that.  With this pseudo-code in mind, let’s get started.  First, we’ll need an additional two combinators that the F# Observable module does not include, which is the ability to delay and observe on the dispatcher thread.  These combinators are simple pass-throughs to the Reactive Extensions which allow us to have a nice pipelining for creating our events.

module Observable =
  let delay (span : TimeSpan) (observable : IObservable<'T>) =
    observable.Delay(span)

  let observeOnDispatcher (observable : IObservable<'T>) =
    observable.ObserveOnDispatcher()

Next, we’ll need to be able to get the position of our mouse in relation to a given UIElement, and this case, it will be the main canvas.

let getPosition (element : #UIElement) (args : MouseEventArgs) =
  let point = args.GetPosition(element)
  (point.X, point.Y)

Now that we have some of the helpers defined, we can define our WPF Window.  In this code, I’ll create a canvas and add it as the content to our Window.  Then I’ll implement the pseudo-code from above into F# to iterate through the characters, create a textbox for each and then set the mouse move to move our text box when there is a reaction.

type TimeFliesWindow() as this =
  inherit Window()

  do this.Title <- "Time files like an arrow"

  let canvas = Canvas(Width=800.0, Height=400.0, Background = Brushes.White) 
  do this.Content <- canvas

  do "F# can react to first class events!"
   |> Seq.iteri(fun i c ->  
          let s = TextBlock(Width=20.0, 
                            Height=30.0, 
                            FontSize=20.0, 
                            Text=string c, 
                            Foreground=Brushes.Black, 
                            Background=Brushes.White)
          canvas.Children.Add(s) |> ignore

          this.MouseMove
          |> Observable.map(getPosition canvas)
          |> Observable.delay (TimeSpan.FromMilliseconds(float i * 100.0))
          |> Observable.observeOnDispatcher
          |> Observable.subscribe(fun (x, y) ->
                 Canvas.SetTop(s, y) 
                 Canvas.SetLeft(s, x + float ( i * 10)))
          |> ignore)

And there you have it, a rather simple example of using F# First Class Events which shows off the composable nature of events and what interesting things we can do with them.

image

Conclusion

Some point in the near future, I’ll come back to explain asynchronous workflows on the client to give further examples on where F# fits.  F# is a great language for writing non-blocking I/O computations.  Using the rich F# First Class events, which are also IObservable<T> instances, we can use both what F# has to offer as well as the integration with the Reactive Extensions for .NET to create even richer experiences. 

This entry was posted in Event-based Porgramming, F#, Reactive Framework. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.facebook.com/people/Scato-Eggen/1576477498 Scato Eggen

    between //Footer and do…, place

    [<STAThread>]

    if you get an error about STA Threads

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Scott,

    Nice example, although from all of that code required to do that makes me long for a framework. After all, we can’t delay that reaction as easily, etc.

    Matt

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Kevin,

    Unfortunately, Silverlight, the Reactive Extensions for .NET and F# will not play together as both have System.IObservable defined in System.Observable.dll and in FSharp.Core.dll. So that means that no Rx goodness in F# Silverlight apps at this time.

    Matt

  • http://lazycoder.com Scott Koon

    Interesting. I found an 8 year old example of this in pure JavaScript, no frameworks.

    http://www.jsr.communitech.net/textcursor.htm

  • Kevin

    Matthew,

    When I try to run this in VS 2008 in F# Interactive (F# version 1.9.9.9), I get compile errors. I believe it is because System.IObservable is defined in both FSharp.Core and in System.Reactive. I had a similar issue when trying to use StreamInsight from F#. What do you recommend to handle this?

    Thanks.

  • http://codebetter.com/blogs/matthew.podwysocki Matthew Podwysocki

    @Phillip,

    Yes, I’ll attach the entire source code required. Sorry I forgot to do that.

    Matt

  • Jake

    This is awesome!
    Thank you!

  • http://trelford.com/blog Phillip Trelford

    Very cute demo; has to be seen running to be truly appreciated.
    Can be run as an F# script in VS2010RC; put the window class in a seperate module, then add the following header and footer:

    // Header
    #r “PresentationCore.dll”
    #r “PresentationFramework.dll”
    #r “WindowsBase.dll”
    #r “System.Xaml.dll”
    #r “UIAutomationTypes.dll”
    #r @”C:\Program Files\Microsoft Reactive Extensions\Redist\DesktopV4\System.CoreEx.dll”
    #r @”C:\Program Files\Microsoft Reactive Extensions\Redist\DesktopV4\System.Reactive.dll”

    open System
    open System.Linq
    open System.Windows
    open System.Windows.Controls
    open System.Windows.Input
    open System.Windows.Media

    // Footer
    do TimeFliesWindow().ShowDialog() |> ignore