Home > Uncategorized > Eventless Programming – Part 2: Computed observables

Eventless Programming – Part 2: Computed observables

Posts in this series:

Welcome back for more dry philosophical musings (I promise there will be an actual working GUI with checkboxes and stuff in the next part). By the way, the code is here: github.com/danielearwicker/eventless/. Also note that I just realised that Getable and Setable would make more sense in a C# context than Readable and Writeable, so I’ve done some renaming.

Last time we got as far as defining a class that could hold a single value in a property called Value, and would fire an event called Changed whenever the value was modified.

Minor detour: for ultimate convenience let’s make a static helper method so we can take full advantage of the type inference that kicks in for generic methods but is sadly absent for constructors of generic classes:

public static class Setable
{
    public static Setable<T> From<T>(T initVal)
    {
        return new Setable<T>(initVal);
    }
}

Now we can really ramp up the hysteria as we add two numbers together:

var i = Setable.From(3);
var j = Setable.From(4);
var sum = Computed.From(() => i + j);

Now, I know it’s exciting but try to contain yourself. The point is, if (and only if) we modify i or j, we need sum to be recomputed (and thus in turn trigger recomputation of anything that consumes sum), just like it would be if these were cells in Excel. This will give us simplicity combined with optimal (that is, minimal) computation. When these are no longer integers but instead complicated data structures or large pieces of UI, it should seem a lot more useful.

So there has to be some magic plumbing involved that notices that we refer to i and j in our definition of sum.

First, Computed.From is just another handy static helper method:

public static class Computed
{
    // Excerpt...
    public static Computed<T> From<T>(Func<T> compute)
    {
        return new Computed<T>(compute);
    }
}

No surprises there, so let’s take a look at the more substantial generic Computed<T> class:

public sealed class Computed<T> : Forwarder<Setable<T>, T>, IGetable<T>, IDisposable
{
    private readonly Func<T> _compute;

    private ISet<IGetable> _subscriptions = Computed.EmptySubscriptions;

    public Computed(Func<T> compute)
        : base(new Setable<T>())
    {
        _compute = compute;
        Recompute();
    }

    private void Recompute()
    {
        var newSubscriptions = new HashSet<IGetable>();

        Computed.Listeners.Push(o => newSubscriptions.Add(o));
        var newVal = _compute();
        Computed.Listeners.Pop();
        Impl.Value = newVal;
        newSubscriptions.Remove(this);
        newSubscriptions.Remove(Impl);

        foreach (var sub in _subscriptions.Where(s => !newSubscriptions.Contains(s)))
            sub.Changed -= Recompute;
        
        foreach (var sub in newSubscriptions.Where(s => !_subscriptions.Contains(s)))
            sub.Changed += Recompute;
        
        _subscriptions = newSubscriptions;
    }

    public T Value
    {
        get { return Impl.Value; }
    }

    public void Dispose()
    {
        foreach (var sub in _subscriptions)
            sub.Changed -= Recompute;
    }
}

First key point: it only implements IGetable, not ISetable, so it has no setter for the value. This makes sense because it holds its own definition of what the value should be at any given moment.

Second (and this is really another detour), it kinda-sorta inherits from Setable, because it needs exactly the same kind of logic for triggering the Changed event only if the new value really is different. But I didn’t want to use full implementation inheritance, because then the features of Setable (such as the Value setter) would all be public, which we’ve already established doesn’t make sense. So instead I inherit a class called Forwarder, specifically designed to encapsulate an implementation of IGetable for the purposes of reuse. We’ll skip over the details of that, but basically if you’re familiar with private inheritance in C++, I’ve roughly simulated that. The end result is that we have a property Impl that holds the Setable we created in the base constructor call, and its Changed event is exposed for us.

Third: the meat of the class lies in its management of a set of other observables to which it subscribes for the Changed event. The currently subscribed observables are held in the _subscriptions set. The constructor calls Recompute the first time, and then Recompute has to make subscriptions so it will be called in the future, like this:

sub.Changed += Recompute;

The cunning part is this:

var newSubscriptions = new HashSet<IGetable>();

Computed.Listeners.Push(o => newSubscriptions.Add(o));
var newVal = _compute();
Computed.Listeners.Pop();
Impl.Value = newVal;

Remember that mystery call to Computed.Listeners.Notify(this) we encountered in part 1? Of course you do – you’ve been barely able to sleep for thinking about it constantly. Really, you look terrible. Just read the rest of this part and then try to get some rest.

Computed.Listeners is just defined as:

internal static readonly ListenerStack<IGetable> Listeners = new ListenerStack<IGetable>();

And ListenerStack is quite a handsome young utility class:

public class ListenerStack<T>
{
    private readonly ThreadLocal<Stack<Action<T>>> Stack =
                    new ThreadLocal<Stack<Action<T>>>(() => new Stack<Action<T>>());

    public void Push(Action<T> listener)
    {
        Stack.Value.Push(listener);
    }

    public void Pop()
    {
        Stack.Value.Pop();
    }

    public void Notify(T obs)
    {
        if (Stack.Value.Count != 0)
            Stack.Value.Peek()(obs);
    }
}

It allows us to notify (pass some value of some type or other) to the top-most listener in a stack of listeners on the current thread. The thread detail is something that knockout.js doesn’t have to worry about, but in the CLR it’s a must. If your UI thread is using Eventless and meanwhile some background worker has also found a use for it, they better not get their wires crossed. It would be a fine mess if the user of a library is careful not to share non-thread-safe objects (such as Setable) between threads, and then the library gets them tangled up anyway.

Computed registers its own listener at the top of the stack of listeners, which is a delegate that just records all the Getables that are notified about. And (as we saw in part one), Getables notify about themselves whenever their values are accessed.

So this means that Computed can (and does) easily find out exactly what Getables are read during the execution of its definition. It can then subscribe to them. In fact by using a HashSet it can pretty rapidly track any changes in its immediate dependencies. And they can be read anywhere while the definition is executing – way down the stack in some other method, without any need to explicitly pass any extra parameters around. The implications of that for constructing software are, I think, rather mind-blowing, and if your mind isn’t blown to smithereens by it, then either (a) you don’t fully get the implications yet, or (b) your mind is already in smithereens from previously discovering this.

One little bit of extra housekeeping:

newSubscriptions.Remove(Impl);

Rationale: it can be handy for a computed to read its old value, and while the definition delegate is executing that old value is there to be read, so we may as well allow it. But doesn’t make sense for it to consequently be dependent on itself.

Bonus Content

The other thing that will be very handy is a collection class. The idea is that (in part 3) we’re going to be displaying the contents of the collection in the UI, so it should be an ordered collection. So, not an ISet or an IDictionary. It must be possible to modify it through random access. In other words, it’s an IList. The CLR has an ObservableCollection class, but I’d like to start with a plain interface and build up from there (that way, it will be easy to bridge to any other frameworks):

public interface ISetableList<T> : IGetable<IList<T>>, IList<T>
{
    event Action<int> Added;
    event Action<int> Updated;
    event Action<int> Removed;
    event Action Cleared;
}

The events that take an int refer to the index within the collection. Note that the collection itself is an IGetable, not an ISetable – you can’t substitute in a new list. You have to clear it down and add new elements. This is no great hardship and simplifies a few things. But it is (effectively) a list of Setables, because you can update individual elements, hence the name ISetableList.

It is also an IList of the given element type. So in our implementation, SetableList, we have to implement all the methods of IList and make them trigger the appropriate events. And of course, whenever the value of an element is read, we better notify the Computed on the top of the stack (if any). Look at the code on Github for the details. for the details.

Okay, so we have most of the building blocks in place. Thank you for your patience. Next stop, part 3 and a working UI

Advertisements
Categories: Uncategorized Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.

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

%d bloggers like this: