Thursday, December 15, 2011

The Disposal Monad

Update: A full listing of Disposal<T> is included in the post after this one.

As mentioned previously, a post on monads seems to be obligatory for any blog involving functional programming. I don't want to repeat the definition or justification, so I'll just point you here for my favourite introduction by the great sigfpe. That was the post that made everything click for me (but YMMV).

In this post I'd like to introduce a new monad of my own device. I haven't seen it used anywhere else, but if anyone knows of prior art I'd love to see it - maybe we can exchange ideas.

The problem

We've all used IDisposable. It's the pattern for representing unmanaged resources, and also works quite nicely for a few other things, like subscriptions to IObservables. The idea is that if you create an object that implements IDisposable, you should call the Dispose method when you're done with it, to make it clean up after itself. Many classes implement IDisposable because they have disposable dependencies that they create themselves.

But what if the object has injected dependencies, and it doesn't know if they're disposable or not? Even if it knows they're disposable, what if they've also been given to another object as dependencies? Who should dispose them?

It's worth noting that this problem is already solved, to some extent. The abstract factory pattern is often used with both a Create method and a Release method; the factory knows where its creations' dependencies have come from, which of them are disposable, and when they can be disposed. IoC containers (being generalised factories) use the same pattern, and all is well.

public class FooFactory : IFooFactory
{
    // private fields and constructor omitted

    public IFoo Create()
    {
        var bar = _BarFactory.Create();
        var foo = new Foo(bar);
        _Bars(foo) = bar;
        return foo;
    }

    public void Release(IFoo obj)
    {
        var foo = obj as Foo;
        if (foo != null)
        {
            foo.Dispose();
            var bar = _Bars(foo);
            _Bars.Remove(foo);
            _BarFactory.Release(bar);
        }
    }
}


The problem with this approach, as I see it, is that it doesn't compose well, which bodes ill for functional programming. Each factory needs to use relatively complex but repetitive logic to keep track of the dependencies of its creations and to call Release on any factories it depends on. Furthermore, it becomes impossible to use factory methods rather than factory objects - we need two methods, not one.

The solution - straightforward version

What we would like to do instead is include, alongside our factory's output, a bundle of IDisposables that should be disposed when we're finished with it. We'll use Disposal<T>, which has three public members:

public class Disposal<T> : IDisposable
{
    // private fields and constructor omitted

    public T Value { get; }
    public IEnumerable<IDisposable> Disposables { get; }

    public void Dispose()
    {
        foreach (var disposable in this.Disposables)
        {
            disposable.Dispose();
        }
    }
}


The factory now looks like this:

public class FooFactory : IFooFactory
{
    // private fields and constructor omitted

    public Disposal<IFoo> Create()
    {
        var barDisposal = _BarFactory.Create();
        var foo = new Foo(barDisposal.Value);
        var disposables = new IDisposable[] { foo }.Concat(barDisposal.Disposables);
        return new Disposal<IFoo>(foo, disposables);
    }
}


We put foo on the front of the list of disposables so that our object graph gets disposed in the opposite order to how it was created.

The consumer will look something like this:

public void ConsumeFoo()
{
    using (var fooDisposal = _FooFactory.Create())
    {
        // use fooDisposal.Value
    }
}

The consumer side's looking pretty good. Our object graph has been wrapped in an IDisposable that encapsulates all of the things we should dispose when we're done with it. But what about the factories? The code to concatenate our disposables is almost as complex as the old version was, and what are we to do about mixing disposable with non-disposable dependencies? Do all factories have to return Disposal<T>, just in case?

The Disposal monad

It turns out that we can do a lot better. Astute readers will already have noticed that the Create method looks very monadic. Let's use LINQ to take advantage of this (using techniques described by Wes Dyer here):

public Disposal<V> SelectMany<U, V>(Func<T, Disposal<U>> intermediateSelector,
                                    Func<T, U, V> resultSelector)
{
    var intermediate = intermediateSelector(this.Value);
    var result = resultSelector(this.Value, intermediate.Value);
    return new Disposal<V>(result, intermediate.Disposables.Concat(this.Disposables));
}

This lets us chain functions of type Func<T, Disposal<U>> - which are just our factory methods - in LINQ queries. FooFactory is already looking simpler:

 public class FooFactory : IFooFactory
{
    // private fields and constructor omitted

    public Disposal<IFoo> Create()
    {

        return from bar in _BarFactory.Create()
               from foo in Disposal.Unit(new Foo(bar))
               select foo;
    }
}


Disposal.Unit is just a static method that lets us wrap a single IDisposable in a Disposal<T>.

We might have factories that represent a transformation on an IFoo; into a FooAdapter, say. FooAdapter isn't disposable, and it doesn't know it's getting a disposable Foo, but we still need our dependencies to be disposed. We need a way to lift a function from IFoo to FooAdapter into one from Disposal<IFoo> to Disposal<FooAdapter>. No problem:

public Disposal<U> Select<U>(Func<T, U> selector)
{
    return new Disposal<U>(selector(this.Value), this.Disposables);
}

C# LINQ queries can only contain one select clause at the end, so we'll use a let clause to call Select. Throwing away the factory for a quick combined example:

return from bar in _BarFactory.Create()
       from foo in Disposal.Unit(new Foo(bar))
       let fooAdapter = new FooAdapter(foo)
       select fooAdapter;

We can now compose multiple single-method factories, some of which return IDisposables, together.

Conclusion

This isn't a perfect solution. There's a definite learning curve on hijacking LINQ for monads other than IEnumerable<T> and its friends IQueryable<T> and IObservable<T>, and I've even met a professed .NET programmer who asked me "Why have you spelled 'link' with a Q?". However, I think the gain in composability and the fact that it drastically reduces the amount of code in factories gives the disposal monad an edge over the alternatives.

No comments:

Post a Comment