Home > C#, delegates, functional > More on delegates vs interfaces

More on delegates vs interfaces

A commenter on yesterday’s post asked about the lack of Dispose. Here’s a way of doing it:

public static IEnumerable<T> AsEnumerable<T>(
    this Tuple<Func<Tuple<bool, T>>, Action> source)
{
    try
    {
        for (var i = source.Item1(); i.Item1; i = source.Item1())
            yield return i.Item2;
    }
    finally { source.Item2(); }
}

public static Tuple<Func<Tuple<bool, T>>, Action> AsTuple<T>(
    this IEnumerator<T> source)
{
    return Tuple.Create<Func<Tuple<bool, T>>, Action>(
        () => source.MoveNext() ? Tuple.Create(true, source.Current)
                                : Tuple.Create(false, default(T)),
        source.Dispose);
}

public static Tuple<Func<Tuple<bool, T>>, Action> AsShared<T>(
    this Tuple<Func<Tuple<bool, T>>, Action> source, int clients)
{
    var padlock = new object();
    return Tuple.Create<Func<Tuple<bool, T>>, Action>(
        () => { lock (padlock) { return source.Item1(); }},
        () => { lock (padlock) { if (--clients == 0) source.Item2(); } }
        );
}

So then we can make something that can be shared between threads.

var mySequence = SomeFancyIteratorMethod();

var shared = mySequence.GetEnumerator().AsTuple().AsShared(4);

// in four other threads...
foreach (var n in shared.AsEnumerable())
{
    // can use 'break' in here
}

But is this necessarily a “good” way to do it?

It’s important to remember that we’re trying to solve two problems at once. Firstly, we need a single method for stepping through the items to be able to lock around it. Secondly, it’s usually more succinct and expressive to be able to write an inline lambda instead of a whole separate named class to implement an interface.

But when your interface of necessity has two operations in it, as this one does, then you’re going to be dealing with nested tuples of delegates, and C#’s type infererence just isn’t powerful enough to make that look pretty. The thing we’re using to represent a disposable iteration is Tuple<Func<Tuple>, Action>, or in other words: a tuple containing two things: a delegate that returns a tuple containing a bool and T, and an delegate that returns nothing. There are no helpful meaningful names on these things, so it starts to get pretty obscure.

This is what custom types (class, struct) are all about. Although tuples are great in short examples, I suspect in most real C# programs, we will continue to prefer custom-declared types so we can give meaningful names to things.

The above code would therefore probably be improved by writing a small class to use instead of Tuple, specifically for holding a GetNext delegate and a Dispose delegate. Apart from that, I think it has an edge over writing out traditional interfaces and classes that implement them, because lambdas and closures are just so much more succinct. A lot of noise in the code disappears because we don’t have to write constructors and fields explicitly.

There are lots of things of value that we can learn from functional programming languages, but I think un-named tuples are probably not the greatest thing.

By the way, there’s not actually anything strictly functional about this whole topic; in a pure functional language, a function never returns a different value the second time you call it with the same arguments.

If you remove that constraint, then a functional language would be nothing special! People talk about how in functional languages, there are higher order functions, which are functions that accept a function and return another function, and functions can be bound to data (closures). Well, in widely-used OO languages, an object is essentially a dictionary of functions (indeed, in JavaScript, that’s exactly what an object is). So string.Trim is a function that takes a dictionary of functions and returns another dictionary of functions. Big deal!

Advertisements
Categories: C#, delegates, functional Tags: , ,
  1. Dax
    October 29, 2009 at 12:25 pm

    Nice followup. I’m just now getting up to speed on Lambdas and was wondering if simply everything should be written that way, especially after reading a few of your previous posts. It’s good to see that I’m not crazy in thinking that there must still be some worthwhile use for classes and interfaces. (Or, well, it would’ve been perhaps even more interesting to see a new way of looking at things that showed classes and interfaces to be completely useless, if such a perspective existed, but I’m guessing it doesn’t).

    Anyway, (armed with the fact that classes aren’t yet “outdated”) posted a simple solution to the Disposal problem back on Skeet’s original posting. Feel free to check it out.

  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: