Home > Uncategorized > Linq To Stupid

Linq To Stupid

This is perhaps the oddest idea I’ve had for a while.

In functional programming, we write functions that return values, and have no side effects. But often it is useful to consider a series of operations that work as a pipeline and “pass forward” their results to some object. Each operation therefore has no return value in the usual sense. It has to have a side-effect on something, or there would be no point in it executing.

We can represent such an operation in C# with the Action<T> delegate. For example:

Action<object> print = v => Console.WriteLine(v);

Now we can imagine a stream of values being passed to that delegate. But this is unsatisfactory, because there is no way to notify that the stream has ended. But we can solve that!

And the stupidest way I can think of (actually it’s quite fun, but almost certainly useless) is as follows. Instead of representing a destination for values by a mere Action<T>, we can use IEnumerable<Action<T>>. That is, we get a sequence of recipients. Bear in mind that to loop through such a thing, we have to get an IEnumerator from it, and when we’re done with it, we Dispose of it. So this properly communicates the fact that the sequence has a beginning and an end.

Now, this raises the question of how you might implement the receiver. The obvious (!) answer is to use yield return.

public static IEnumerable<Action<object>> Print()
{
    for (;;)
    {
        object item = null;
        yield return input => item = input;

        Console.WriteLine(item);
    }
}

This is quite a curious beast. It’s a function that reads a series of values from it’s own return value. You see what I mean by stupid. And how would you pass a sequence of values to such a beast?

public static void Read<TIn>(this IEnumerable<Action<TIn>> destination, IEnumerable<TIn> source)
{
    var e = destination.GetEnumerator();

    try
    {
        foreach (var item in source)
        {
            if (!e.MoveNext())
                break;

            e.Current(item);
        }
    }
    finally
    {
        e.Dispose();
    }
}

The above is a general helper for driving any receiver. For example, applying it to our Print receiver:

Print().Read(new object[] {1, 6, 23, 5, 7, 2});

Now, there’s some co- and contravariance misery here. My input array is of type object, because that is what Print expects (because Console.WriteLine can print anything). But we ought to be able to pass an array of int, because ints are objects. This will perhaps be solved by C# 4.0 (I would check, but this code is too silly to bother).

Now, the odd thing is that we can now write particular versions of the Linq operators such as Select, Where, and so on. For example, here’s the specialized version of Select:

public static IEnumerable<Action<TIn>> Select<TIn, TOut>(
    this IEnumerable<Action<TOut>> destination, Func<TIn, TOut> selector)
{
    foreach (var action in destination)
    {
        TIn item = default(TIn);
        yield return input => item = input;

        action(selector(item));
    }
}

Where is similar.One slight drawback is that you have to write your linq query backwards:

Action<int> printTotal = total => Console.WriteLine("Total of squares greater than 4 is: " + total);

printTotal.Aggregate((l, r) => l + r)
    .Where(square => square > 4)
    .Select((int n) => n*n)
    .Read(new[] {1, 6, 23, 5, 7, 2});

Each step is the recipient of whatever is produced by what comes after it. I do not call it Linq to Stupid lightly.

But there are some other things that we can do here that Linq cannot do!

For instance, we can multicast. That is, we can attach several receivers together to appear like a single receiver, and then push a stream of values into them:

public static IEnumerable<Action<TIn>> Multicast<TIn>(
    this IEnumerable<IEnumerable<Action<TIn>>> destinations)
{
    var enumerators = destinations.Select(d => d.GetEnumerator()).ToList();

    try
    {
        for (; ; )
        {
            TIn item = default(TIn);
            yield return input => item = input;

            foreach (var enumerator in enumerators)
            {
                if (!enumerator.MoveNext())
                    break;

                enumerator.Current(item);
            }
        }
    }
    finally
    {
        enumerators.Dispose();
    }
}

This has been a hot topic recently, with the arrival of Microsoft’s Rx framework, and before that community efforts like Push Linq. Those libraries are predicated on the assumption that there are certain things you can’t do with IEnumerable.

I think I’ve demonstrated that in fact you can do a forward-passing pipeline, if you’re prepared to use IEnumerable in the stupidest possible way.

Advertisements
Categories: Uncategorized Tags: , ,
  1. Trillian
    April 11, 2010 at 1:43 am

    This freaks me out, but it’s really cool, in an uncool way!

  1. February 10, 2010 at 3:50 pm

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: