Reactive programming (II.) - Introducing Reactive LINQ
In this article I'm going to introduce my project Reactive LINQ. This is largely inspired by the ideas that come from functional reactive programming in the Haskell language and from functionality that's available for working in events in F#. I introduced these ideas in my previous article in this mini-series, so if you're interested in learning more about these interesting technologies, you should definitely read the previous article about First class events in F#.
In this article, we're going to look how to apply the same ideas to the C# language and how to use LINQ queries for processing events. This article is just another my article that shows how to implement some idea from functional programming in C#. I believe this will once again show that the new features in the C# 3.0 aren't just about querying databases, but are more widely useful even in situations that are not directly related to data-processing.
The idea: Event is a stream
The key idea that is important for the project is that we can look at .NET events in a different way.
We can say that events are actually streams that carry some values, even though these values are not
available all the time. They appear in the stream once in a while when the event occurs.
Let's take for example a
MouseDown event. This can be viewed as a stream that contains
information about mouse clicks - the button together with X and Y location. A new value in the stream
appears every time user clicks on the control. Now that we have a stream of values, you can see that
this is quite similar to for example a collection of values.
Processing events with queries
I'll show you a simple example first and then we're going to look how things work behind the scene. We'll write a very simple game - the form will show you a maze (just like the one you can see on the screenshot) and your goal will be to move the mouse cursor from the starting point (on the left) to the target point (on the right). You have to follow the white path and if you move the cursor on the grass, the game ends.
Our implementation will be very simple - it will load the bitmap into the memory and every time you move the mouse, it will check whether the mouse is still on the path (I guess you could cheat if you moved the mouse quickly, but that's not the point now!)
In the usual C# programming style, you'd register an event handler for the
event and implemented a method that reads the color from the bitmap and tests whether the
color is white. If not, it would show a message and set some flag to end the game. Then it would check
whether we're close to the target point and if yes, it would display some congratulations box.
We'll implement the same thing using two Reactive LINQ queries.
Following the path
The first query will test whether the cursor is still on the path (by checking the color below the cursor).
This means that we'll take the event stream coming from the
MouseMove event. A value will
appear in the stream whenever the cursor position changes. In the first part, we're interested only in
the cases where the cursor moved out of the path, so we'll use the
where clause to filter
values that are on the path. Let's take a look at the first part of the code:
// Move cursor to the start location and load the bitmap Cursor.Position = this.PointToScreen(new Point(150, 200)); Bitmap bmp = (Bitmap)this.BackgroundImage; // Create stream for the 'MouseMove' event and // filter events when the mouse cursor is on the path var eOut = from me in this.AttachEvent<MouseEventArgs>("MouseMove") let clr = bmp.GetPixel(me.X, me.Y) where clr.R != 255 || clr.G != 255 || clr.B != 255 select "Oops!\nYou stepped on the grass!";
We start by loading the bitmap and by moving the cursor to the starting position in the maze.
The second statement is much more interesting. It is a LINQ query that processes values called
me. The type of
MouseEventArgs, which suggests that
the value is created every time the user moves the mouse. The source of these values is
AttachEvent method. In an ideal world, we could just write
this.MouseMove, but in C# you can't use event as an object (for example as a method argument),
AttachEvent method contains some reflection code to create a value that
represents the event. I'll talk about these values in a second.
Once we have
MouseEventArgs value available, we can use its properties to
check whether the cursor is still following the path. We first use the LINQ
let clause to
store the color in a variable
clr and then use
where to test whether the
color is other than white. Once the user moves the mouse out of the path, this becomes true and
select clause is triggered. It simply returns a message that we want to show
to the user.
Reactive LINQ under the hood
Under the hood, this is translated to an expression that calls
LINQ operators and looks roughly like this (the
let clause makes it a bit difficult, but
we don't have to care about that, because we're interested only in the structure):
var eOut = this.AttachEvent<MouseEventArgs>("MouseMove") .Where(args => /* some lambda expression */ ) .Select(args => /* some lambda expression */ );
This means, that we create some representation of event using
AttachEvent and the
Reactive LINQ library provides standard LINQ query operators for this representation.
Select take an event and a function as arguments and return
a new event. The type that I use for representing events is an interface type called
This interface has methods for adding and removing event handlers, but you'll typically never use them directly,
because you can work with it using operators like
Where and others.
AttachEvent method is added as an extension method to Windows Forms controls and takes
a name of the event as an argument. You also have to specify the specific
EventArgs type used
with this event. The result in our previous example is
This represents an event that gives us
MouseEventArgs value when it is triggered. Then we
use two LINQ operators that do the following thing:
- Where takes an event that gives us value of type
TValueand a delegate (lambda expression). The result of this operator is an event that contains the same value
TValue, but it is triggered only when the delegate returns true. In our maze example, the event returned by
Wherewill be triggered only when user moves out of the path.
- Select transforms an event that gives values of type
TSourceinto an event that gives values of type
TTarget. It takes a function that knows how to create the target value from the source value. The returned event is triggered each time the source one is triggered. In our example, the function simply returns the same
stringvalue and ignores the
MouseEventArgsthat it gets as an argument.
This means that the overall result of the LINQ query from the previous example is
This event is triggered when the user mouse the cursor out of the path and it will contain a string value (a message)
that we want to show to the user. We'll do this in the next section.
Reacting to events
We'll first implement a handler that will show a message box when the user fails and later we'll finish the
second part of the game, which is testing whether the cursor is close to the target point. Adding handler to
the event value
eOut is quite easy:
eOut.Listen(str => MessageBox.Show(str));
This uses another operator (extension method) available for the
IEvent<T> type, which is
Listen. Similarly as
Event.listen in F# (see the previous part of this series)
this can be used at the end of the event query. It will be called when the event is raised and it can
react to the event in any way. In our example, we use a lambda expression that takes the string
(that is the message coming from the event) and displays it using
Combining multiple events
The code for testing whether the mouse is close to the target point will be very similar to the
query we've seen earlier. Only the condition that tests whether the event should be triggered
where) will differ. Again, we'll write this as an event that gives
string value when it occurs, so it makes a good sense to reuse the part that
shows the message to the user.
Testing the target point
Let's start with the query. Similarly as in the first case, it'll query values comming from the
MouseMove event. Next, it calculates the distance between the target point, which is
(450, 250) and the current mouse location
(me.X, me.Y). We'll test
whether this distance is small enough and if yes, we'll trigger the event with a message that
the game was successfully completed:
// Filter events when mouse is far from the target // if the cursor is close, we return a message var eFinish = from me in this.AttachEvent<MouseEventArgs>("MouseMove") let dist = Math.Pow(450 - me.X, 2) + Math.Pow(250 - me.Y, 2) where dist < 250 select "Congratulations!\nYou made it to the end!";
As I mentioned earlier, the query is essentially the same as the first one, with the exception
that it has a different condition and returns a different message. An important thing to note is
eFinish value has the same type as the
eOut value - both of
So far, we have two events - one that gives us a message when the user fails and the second one that gives us a message when the user succeeds. The reaction to both of the events will be exactly the same. We'll display the message and then stop further processing of the events, because we don't want to show the messages again (we ignored this issue in the first version).
Because we want to use a single handler for both of the events, we'd like to combine
them into a single event in some way. This can be done using
from the static class
Reactive. This class contains various other methods
for working with events and we'll talk about some of them in later parts of this series.
Once we combine the events, we can again use
Listen to write the code that
reacts to the event. Note that if you're writing the code, you'll need to remove the previous line
where we implemented a reaction to the
eOut event, because now we'll react
to both of the events at once:
// Wait for either of the events // then stop all processing and show the message var eMerged = Reactive.Merge(eOut, eFinish); eMerged.First().Listen(str => MessageBox.Show(str));
We start by merging the events into an event called
eMerged. This event has again a type
IEvent<string>. It will be triggered whenever either of the two merged events
is triggered. This means that the
eMerged event will be triggered when the user moves outside
of the path or when the cursor is close to the target point (possibly several times).
However, we're only interested in the first occurrence. Once either of these two occurs, we
just want to show the message box and we're not interested in any further events. This can
be written using another LINQ operator called
First. The event returned
First will be triggered only once, so we can react to this event using
In this article, we've looked at the basic examples of my Reactive LINQ project. This project is largely inspired by functional reactive programming in Haskell  and also by first-class events in the F# language . I wrote about the F# implementation in the previous series, so if you skipped that, you may want to read it too.
We looked at the basic query operators that Reactive LINQ provides for events.
In particular, we've looked at
Select that can be accessed
using the nice LINQ query syntax and at combinators like
First (for selecting the first event),
Merge (for merging a couple of events) and
Listen (for reacting to an event).
In the next series, we're going to look at some more very useful operators for working with events and
in particular at switching operators that allow us to change the way events are handled
Downloads and implementation notes
The current implementation is really just a prototype to demonstrate the idea. This means that it
probably still contains many issues. One of the possible issues is with removing event handlers
when they are not needed anymore. For example, after the
First operator is triggered
for the first time, it needs to unregister itself from the source event, because it doesn't need
to listen to them anymore. Currently, this is implemented using reference-counting like technique.
This means that events count how many listeners they have and they unregister when there is no
listener. This is of course problematic, because you may create loops. In the future, I'd like
to make the implementation more stable and I'm planning to take a look at the Weak Event Pattern ,
which is used in WPF. Since this is a prototype, feel free to do anything with it and let me know
if you have any suggestions or ideas how it could be improved!
- Reactive programming (I.) - First class events in F#
- Reactive programming (II.) - Introducing Reactive LINQ
- Reactive Programming (III.) - Useful Reactive LINQ Operators
- Reactive Programming (IV.) - Developing reactive game in Reactive LINQ
-  Functional Reactive Programming Research - Haskell Web Page
-  F# First Class Events: Simplicity and Compositionality in Imperative Reactive Programming - Don Syme's WebLog on the F# Language and Related Topics
-  Weak Event Patterns - Windows Presentation Foundation at MSDN