6

Background

I recently read that .NET 4's System.String class has a new overload of the Join method. This new overload takes a separator, and an IEnumerable<T> which allows arbitrary collections to be joined into a single string without the need to convert to an intermediate string array.

Cool! That means I can now do this:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0);
var list = string.Join(",",evenNums);

...instead of this:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .Select(i => i.ToString())
    .ToArray();
var list = string.Join(",", evenNums);

...thus saving on a conversion of every item to a string, and then the allocation of an array.

The Problem

However, being a fan of the functional style of programming in general, and method chaining in C# in particular, I would prefer to be able to write something like this:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .string.Join(",");

This is not legal C# though. Yes, I could do it with Enumerable.Aggregate, and yes, I could do it with my own Join extension method), but those approaches are hard to read/inefficient and feel like a cop-out (respectively) so I would like to try and do it a different way. The closest I've managed to get so far, is this:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo(
        Functional.Curry<string, IEnumerable<object>, string>
            (string.Join)(",")
    );

...using the following extension methods:

public static class Functional
{
    public static TRslt
    ApplyTo<TArg, TRslt>(this TArg arg, Func<TArg, TRslt> func)
    {
        return func(arg);
    }

    public static Func<T1, Func<T2, TResult>>
    Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curried
            = f => x => y => f(x, y);
        return curried(func);
    }
}

This is quite verbose, requires explicit definition of the parameters and return type of the string.Join overload I want to use, and relies upon C#4's variance features because we are defining one of the arguments as IEnumerable rather than IEnumerable.

The Challenge

Can you find a neater way of achieving this using the method-chaining style of programming?

This challenge is about trying to find a terse way, in C#, to curry a function that has multiple overloads - and it's just for fun!

Community
  • 1
  • 1
Damian Powell
  • 8,655
  • 7
  • 48
  • 58
  • 3
    Although this is a very interesting question, there is no good reason not to use an extension method. Maybe you should make more obvious what other options are there and why they are interesting/better to have someone take a thought at this. – Dykam Apr 17 '10 at 15:09
  • I was pleasantly surprised when my Microsoft Connect request for an overload with IEnumerable turned up in .NET 4 with the even more useful overload IEnumerable. – Ian Mercer Apr 17 '10 at 17:40

3 Answers3

6

What's wrong with:

var list = string.Join(",",Enumerable.Range(1, 100).Where(i => i%2 == 0));

I don't really see how this is not functional programming. It's less chaining, true. But functional program is not about chaining, it's about writing more declarative statements, which this is doing in my book. Of course, if you really want it to be chained you can do this: What is the LINQ way to implode/join a string array?

Community
  • 1
  • 1
tster
  • 17,883
  • 5
  • 53
  • 72
  • 2
    The point is not really about using string.Join; it's merely a convenient example. The point is to find a tidy way to do currying and/or partial application in C#. Currying and partial application most definitely *are* functional programming topics. The reason the question insists upon method chaining is in order to discourage the obvious answer of wrapping the whole thing as an argument to string.Join - clearly that didn't work though since that is exactly what you suggested as the answer! – Damian Powell Apr 17 '10 at 17:23
  • 2
    My point is that method chaining is not more "functional" than passing the result of one function to another function. – tster Apr 18 '10 at 16:12
1

I'm not sure why you need the additional Curry method from your example. Simply using another Lambda produces a more terse option.

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo((x) => { return string.Join(",", x); })
Reid Evans
  • 1,611
  • 15
  • 19
0

After five years it's still unanswered, let's try this!

Efficient, with no external code, completely chaining and readable:

var list = Enumerable.Range(1, 100000)
    .Where(i => i % 2 == 0)
    .Aggregate(new StringBuilder(), (prev, i) => prev.AppendFormat(",{0}",i))
    .Remove(0,1)
    .ToString();
Giulio Caccin
  • 2,962
  • 6
  • 36
  • 57