Friday, December 16, 2011

Listing: the Disposal monad

I received some feedback on yesterday's post, pointing out that it was hard to get the whole story of Disposal<T>'s implementation. Here's the full listing:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace DisposalMonad
{
    public static class Disposal
    {
        public static Disposal<T> Unit<T>(T value) where T : class, IDisposable
        {
            return new Disposal<T>(value, Enumerable.Repeat(value, 1));
        }
    }

    public class Disposal<T> : IDisposable
    {
        private readonly T _Value;
        private readonly IEnumerable<IDisposable> _Disposables;

        internal Disposal(T value, IEnumerable<IDisposable> disposables)
        {
            _Value = value;
            _Disposables = new ReadOnlyCollection<IDisposable>(disposables.ToList());
        }

        public T Value
        {
            get { return _Value; }
        }

        public IEnumerable<IDisposable> Disposables
        {
            get { return _Disposables; }
        }

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

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

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

An interesting side point is that Disposal<T> can be turned into something a bit like a linear type - just remove both of the public properties and replace them with the following methods:

public void UseWith(Action<T> action)
{
    using (this)
    {
        action(_Value);
    }
}

public U UseIn<U>(Func<T, U> selector)
{
    using (this)
    {
        return selector(_Value);
    }
}

This might be overkill, though. It makes the class much harder to use and I'm not sure it adds much that will be useful to most applications.

No comments:

Post a Comment