I’ve been spending some time with Reactive Extensions for the last months. I came across with such framework from Erik Meijer’s live session and since then, I’ve been trying to understand what Rx exactly tries to offer us as developers.
Rx turns around the duality that exists between IEnumerable and IObservable. This last one, is one of the new features .NET Framework 4.0 offers. I told about it in this post. Now, the idea arises on how is exactly such duality –mathematical duality, in fact- between both interfaces since it’s highly important to understand it in order to be able to apply Rx successfully to our developments. Before .NET Framework 4.0 we have been dealing objects with LINQ to Objects, so if IObservable deals with asynchronous programming model as well as push-based notifications –like events- then why not enhance LINQ for dealing with Events and async callbacks? That’s the reason Rx states.
Let’s talk about IEnumerable interface.
IEnumerable objects just exposes enumerator represented by IEnumerator interface for supporting a simple iteration over a collection of given generic type.We’ll not be able to execute the foreach statement unless the collection implements IEnumerator, actually there wouldn’t be a way to do this without IEnumerator implementation.
The example showed above represents a iteration with a collection of customers that has invoices. Whatever the type of customer object was -List<T> for example- it must implement IEnumerable<Customer> interface. IEnumerable<T> exposes, as said before, an IEnumerator object as we can see at IEnumerable definition:
As exposed above, an IEnumerator<T> object is being returned each time GetEnumerator() is called. On the other hand, IEnumerator definition is: (let me obviate generics right now)
Now, a couple of things we could say about this last code snippet. At first, note that just 2 of those 3 exposed methods are “really necessary”, Current and MoveNext. Reset method is provided for COM compatibility thus forget it.
For the second reason let me return to customer’s invoice sample showed above. Let’s says we are dealing with unpaid invoices – we need money!! – The extension method “Where” of customers objects filtered all the clients based on a predicate represented by hasInvoice generic function. foreach statements is iterating defaulter customers –we want their names – for contacting them. Let’s say we’ve got 4 defaulter customers so foreach statement is going to call both MoveNext and Current methods of IEnumerator interface in order to iterate res collection object. foreach statement asks enumerator for returning next customer –MoveNext- and after enumerator said true –it finds and moves to the next one- such customer will be returned –via Current property -. Such process will take place 4 times -for each defaulter customer- until MoveNext method returns false. In such case, the enumerator is after the last element –last customer in our case –.
That’s all.That’s what IEnumerable interface really does, nothing more.
All right, now introduce me duality…
Thus, let’s talk about duality, mathematical duality. We can find some examples in other fields like De Morgan’s laws used in formal logic or limit and colimit of given function in calculus and analysis. In .NET Framework IEnumerable is dual with IObservable, in other words, LINQ to Objets is dual with LINQ to Events. Take a look at the following images which represents an IEnumerable function:
Image1. IEnumerable function
Image 2. IEnumerable function
Both IEnumerator and IObservable approaches deal with a collection however the main difference is how each one deals with such collection. While IEnumerable approach is continuously asking for the next object, IObservable provides such object to its subscribers. Check the following images which represents IObservable function.
Image 3. IObservable function
Image 4. IObservable function
If you read my post Observer Design Pattern with .NET Framework 4.0 you may be familiarized with both IObservable and IObserver interfaces. Therefore, whatsoever duality of both models allows is to materialize given IEnumerable collection to IObservable one and vice versa.
Let’s take the IEnumerable definition again keeping in mind the functions images showed above.
Image 5. IEnumerable and IEnumerator definition
For turning IEnumerable into a IObservable, the first step is to change the duals methods. The equivalent method for IObservable would be Subscribe() instead of GetEnumerator(). GetEnumerator() gets no arguments so Subscriber will return void.
Image 6. IEnumerable.GetEnumerator() turns into method IObservable.Subscribe() returning void
GetEnumerator() returns IEnumerator<T> so Subscribe() will receive its equivalent by parameter.
Image 7. IEnumerable.GetEnumerator() turns into method IObservable.Subscribe() passing IObserver<T> object as parameter
So, by the moment we are getting the following:
Image 8. IObservable.Subscribe() final transformation.
Now, talk about IObserver –note that IObservable is not still finished –. IObserver duality is IEnumerator so let’s take it. Current property will be turned into a OnNext property right now. While Current is a getter property OnNext will be setter one.
Image 9. Turning IEnumerator into IObserver.
But what’s is actually a property but a method? therefore, the final contract for OnNext item will be:
Image 10. Turning IEnumerator into IObserver.
On the other hand MoveNext() method arises a couple of questions. Its contract tell us that it just returns bool value but it is not absolutely true. MoveNext() may throw an exception such as InvalidOperationException, for instance. So what we are getting here is two different return types, either bool or Exception. So its duality will be actually two methods: OnError() for an exception and OnComplete() when MoveNext returns false. You may wondering about what’s happening when MoveNext() returns true. Actually, think about it carefully. Does such case concerns to subscribers? no it does not or we might say it is dual to OnNext() method. So finally will get following contracts:
Image 11. Turning IEnumerator into IObserver.
Now, both IObservable and IObserver interfaces are taking form. Withdrawing annotations from the image both interfaces will face as following:
Image 12. IObserver and IObservable final definition.
If we go one step forward we may notice that we forgot one interface in the road. We have seen how IEnumerable.GetEnumerator turns into IObservable.Subscribe() step by step. However, what previously was IEnumerator and have became an IObserver both implements IDisposable interface as well. Actually we could say that IEnumerable.GetEnumerator() could be defined as:
So in its own duality we may turn such statement into:
Leaving no interfaces as a return type that is void. On the other hand, during such transformation we got a Subscribe() method but not UnSubscribe one. In the same way we want an observer to be subscribed to observable collection we might want to unsubscribe it. It could easily resolve by leaving IDisposable interface as returning type as follows:
Image 13. Each IObserver object is IDisposable.
What is really interesting here beyond conceptual/theorist explanation is that any developer could understand that an event or any other push-based notification or async technology can be handled with LINQ in the same way that whatever IEnumerable oject does. Silverlight, WPF, SQL Server Integration Services are just an examples of technologies that uses asynchronous callbacks.