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).
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
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?
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
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
No comments:
Post a Comment