Years ago, I wrote a small console application in VB6 that simulated a digital circuit by using gates. It was a great way to learn OOP, because each class directly corresponded with a real-life entity, and the behavior was something slightly more useful than dogs barking or cows mooing. Wiring up some events and a basic input form, I was able to simulate the results of fairly complex circuits. The problematic part of this is that because of the event model, it took forever to calculate, and hundreds of lines of code.

Fast-forward to last year when I heard about Reactive Extensions for .NET, a true implementation of a push-style observer pattern built on top of IEnumerable<>. Coming from the land of events and synchronous programming, it was difficult to wrap my head around, but like many concepts, I eventually “got it”. However, I would have reached those revelations sooner if there were some better tutorials out there. This is an attempt at a better tutorial for Rx, or at least a simpler one.

Most Rx tutorials read something like “Ok, you have some Xs and Ys. Now this is how you merge them using fifty chained methods.” Once you’ve used Rx, this is easy enough to grasp, but having no idea what you can actually do with Rx, it’s difficult to start at such an advanced level. So, this tutorial will take something simple and show you how pushing values instead of pulling them can make programming easier.

Ok, let’s take an AND gate. AND Gate An AND Gate takes two input bits, each a 0 or 1, and outputs a 1 only if both inputs are 1. If either input is 0, then the output is 0. The output of this gate can then be the final circuit output, or it can be placed as the input to another gate. The combination of all the gates in a circuit outputs some final value based on the inputs to that circuit. This tutorial will only simulate a single AND gate, leaving the rest to future posts.

So, what does Rx do that will help us here? Well, the AND gate takes input values, which may change over time (either due to user interaction, or the results of other gates changing value). We don’t want to sit there and poll the input constantly to see if it changes, we only want to recalculate our output value if the inputs change. So, we could set up some events to model this, or we could simply push new values to the gate. Rx is the glue that allows you to push values to any observers you have.

Rx requires us to rethink the way inputs occur. With Windows Forms (And even WPF), we tend to think about user interaction as isolated events, such as “clicked button 1, clicked checkbox, clicked button 2″. In reality, user interaction is better modeled as a stream of sorts. For example, if the user clicks two buttons, there are hundreds of MouseMove events in-between, and maybe a tab change, window resize, or any number of things inbetween. User interaction is therefore a constant stream of data about what the user is doing with all input devices. Likewise, a digital circuit’s input is not a series of isolated events, it’s a constant stream of 1s or 0s, changing due to the result of something happening in the system as a whole. So, instead of constantly looking at the input value to detect a change, why could we not simply subscribe to the stream of input information? We can then take any new input and make decisions at that time, saving unnecessary code and processing time.

So, let’s think about some of the entities we can model here. An AND Gate would have two Inputs, which we would need to subscribe to. Any time one of the inputs gives us an updated value, we should update our output value. However, just like our inputs are considered a stream, our output should also be a stream, since the values are ever-changing. Instead of using Boolean true/false, we should probably make a Bit type, which could be high or low, so perhaps an enumeration would work for this. Also, we would like for this to work with other gates in the future, so we will make a generic Gate class, and use inheritance to provide custom functionality. So, let’s get started.

public enum Bit { Low = 0, High = 1 }
public class Input
{
     public void SetValue(Bit value)
     {
          // something to update the value stream
     }
}

Ok, so here we define our Bit type, as well as exposing a basic method for allowing the input value to be changed. Remember, though, that we are not simply toggling a value back and forth, we are publishing the new value to a stream. So, how do we model such a stream? This is where Rx comes into play. The core of Rx is an interface called IObservable<T>. The interface is simple, it simply provides a way to subscribe to a stream of T. So, for our purposes, any inputs or outputs can be represented by a stream of Bit, and in Rx by IObservable<Bit>. However, you may know that we can’t simply add a parameter of type IObservable<Bit> to the Input class, since it is an interface and we need a concrete type. We could implement IObservable in our class, but then we would have to keep track of subscribers and provide a way to dispose of the subscription. In production code, this would be the way to go, but for our purposes we will use a class the Rx team provides for this, Subject<Bit>. This will simply give us a quick way to get from a Bit value to a stream of Bit values. So, let’s update our Input code:

public class Input
{
     private Subject<Bit> valueStream = new Subject<Bit>();
     public IObservable<Bit> Value { get { return valueStream; } }
     public void SetValue(Bit value)
     {
          valueStream.OnNext(value);
     }
}

So, here we create a private stream of Bit values called valueStream. We expose the stream’s subscription ability (IObservable) through the Value property. And whenever a value is set, we simply tell the stream to append that value. Now, our Input class has everything it needs to operate. It exposes a stream of values anything can subscribe to, and updates the stream anytime a new value is set. Now that our Input is functional, let’s make a generic gate:

class Gate
{
     public Gate(IObservable<Bit> x, IObservable<Bit> y)
     {
          valueStream = x.CombineLatest(y, Calculate);
     }
     protected IObservable<Bit> valueStream;
     public IObservable<Bit> Value { get { return valueStream; } }
     protected virtual Bit Calculate(Bit x, Bit y)
     {
          return Bit.Low;
     }
}

So, you can see a similar pattern here for the Gate’s output value, using valueStream and Value. This gate can be subscribed to just like any Input. The main difference is that we have to provide a Gate two IObservable<Bit> streams that it uses to calculate its output. The line valueStream = x.CombineLatest(y, Calculate); simply means that it takes the X and Y inputs, and uses the latest value of each to determine the next output, assigning that value directly to the output stream. You may notice we didn’t have to “handle” any event, or explicitly tell it to output the new values. It acts much like a pipeline, simply outputting whenever it gets a new input, based on the results of the Calculate() method. In the case of a default Gate, we will simply output low values no matter what the inputs are.

Now that we have a generic gate, we can easily implement an AndGate:

class AndGate : Gate
{
	public AndGate(IObservable<Bit> x, IObservable<Bit> y) : base(x, y) { }
	protected override Bit Calculate(Bit x, Bit y)
	{
		return (x == Bit.High && y == Bit.High) ? Bit.High : Bit.Low;
	}
}

As you can see, we told it to reference the base constructor, and overrode the Calculate() method that uses the logic of the AND gate. New gates, such as OR, XOR, NAND, etc. can be created in the same way. Now, we test it by wiring up some inputs, defining our subscription to the AND gate (which in this case simply writes it to the console), and then changes the input values so you can see it work:

class Program
{
	static void Main(string[] args)
	{
		Input a = new Input();
		Input b = new Input();
		AndGate a1 = new AndGate(a.Value, b.Value);
		a1.Value.Subscribe(z => Console.WriteLine(z.ToString()));

		a.SetValue(Bit.High);
		b.SetValue(Bit.High); // High + High = High
		b.SetValue(Bit.Low); // High + Low = Low
		b.SetValue(Bit.High); // High + High = High
		a.SetValue(Bit.Low); // Low + High = Low
		b.SetValue(Bit.Low); // Low + Low = Low

		Console.WriteLine("Press any key to exit");
		Console.ReadKey();
	}
}

And, running it, we see that it works:

High
Low
High
Low
Low
Press any key to exit

In a later post, we will show how to wire up multiple gates, and go a bit more in detail about Rx.

Note: Before this tutorial, you should have installed the Rx framework, and referenced the libraries System.Reactive and System.CoreEx.