Show Blogger Panel Hide Blogger Panel
Alex Yakunin

September 25, 2009

Currying in C#

I'd like to show a very simple, but really magical T4 template added by Denis Krjuchkov to Xtensive.Core library. But let's list its output first:
using System;

namespace Xtensive.Core.Helpers
{
  /// <summary>
  /// Extension methods for binding delegates to parameters.
  /// </summary>
  public static class DelegateBindExtensions
  {
    /// <summary>Binds first 1 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Func<TResult> Bind<T1, TResult>(this Func<T1, TResult> d, T1 arg1)
    {
      return () => d.Invoke(arg1);
    }

    /// <summary>Binds first 1 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Func<T2, TResult> Bind<T1, T2, TResult>(this Func<T1, T2, TResult> d, T1 arg1)
    {
      return (arg2) => d.Invoke(arg1, arg2);
    }

    /// <summary>Binds first 2 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Func<TResult> Bind<T1, T2, TResult>(this Func<T1, T2, TResult> d, T1 arg1, T2 arg2)
    {
      return () => d.Invoke(arg1, arg2);
    }

    /// <summary>Binds first 1 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Func<T2, T3, TResult> Bind<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> d, T1 arg1)
    {
      return (arg2, arg3) => d.Invoke(arg1, arg2, arg3);
    }

    /// <summary>Binds first 2 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Func<T3, TResult> Bind<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> d, T1 arg1, T2 arg2)
    {
      return (arg3) => d.Invoke(arg1, arg2, arg3);
    }

    ...

    /// <summary>Binds first 8 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Action<T9> Bind<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> d, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
    {
      return (arg9) => d.Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
    }

    /// <summary>Binds first 9 argument(s) to specified delegate.</summary>
    /// <returns> A delegate that takes the rest of arguments of original delegate.</returns>
    public static Action Bind<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> d, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9)
    {
      return () => d.Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
    }
  }
}

This template allows "currying" delegates of Action<...> and Func<...> types by binding first N arguments out of M there. Each of Bind methods returns a delegate with M-N arguments, which invocation is similar to invocation of the original delegate with the first N arguments set to specified values.

Here is an example:
var add = (a, b) => a + b;
var addOne = add.Bind(1); // addOne = b => 1 + b;
Console.WriteLine(addOne(2)); // Prints 3

var multiplyAdd = (a, b, c) => a + b * c;
var multiply2Add3 = multiplyAdd.Bind(3, 2); // multiply2Add3 = c => 3 + 2 * c;
Console.WriteLine(multiply2Add3(1)); // Prints 5

So it is really simple to use this. Here is an alternative (check out the Curry() code they use), which is closer to commonly known currying, but there are more delegate creation operations, especially - on binding multiple arguments. E.g. calling add.Curry()(1) implies two delegate creations: first call returns Func<int, Func<int, int>>, and the second returns Func<int, int>. In our case there will be just one delegate. Accordingly, there will be 4 delegates in case with addMultiply.Curry()(2)Curry(3), and just one in our case.

Finally, here is T4 template generating Bind extension methods:
<#@ output extension="cs" #>
<#@ template language="C# 3.5" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#
  int NumberOfParameters = 9;

  Action<int, int> WriteParametersDeclaration = delegate(int firstIndex, int lastIndex) {
    if (firstIndex > lastIndex)
      return;
    for (int k = firstIndex; k < lastIndex; k++)
      Write(string.Format("T{0} arg{0}, ", k));
    Write(string.Format("T{0} arg{0}", lastIndex));
  };

  Action<int, int, bool> WriteGenericParameters = delegate(int firstIndex, int lastIndex, bool forAction) {
    if (firstIndex > lastIndex) {
      if (!forAction)
        Write("<TResult>");
      return;
    }
    Write("<");
    if (forAction) {
      for (int k = firstIndex; k < lastIndex; k++)
        Write(string.Format("T{0}, ", k));
      Write(string.Format("T{0}", lastIndex));
    }
    else {
      for (int k = firstIndex; k <= lastIndex; k++)
        Write(string.Format("T{0}, ", k));
      Write("TResult");
    }
    Write(">");
  };

  Action<int, int> WriteParameters = delegate(int firstIndex, int lastIndex) {
    if (firstIndex > lastIndex)
      return;
    for (int k = firstIndex; k < lastIndex; k++)
      Write(string.Format("arg{0}, ", k));
    Write(string.Format("arg{0}", lastIndex));
  };
  
  Action<bool> WriteType = delegate (bool forAction) {
 Write(forAction ? "Action" : "Func");
  };
  
  Action<int, int, bool> WriteFirstParameter = delegate(int firstIndex, int lastIndex, bool forAction) {
    Write("this ");
    WriteType(forAction);
    WriteGenericParameters(firstIndex, lastIndex, forAction);
    Write(" d, ");
  };

  Action<bool> WriteMethods = delegate(bool forAction) {
    for (int total = 1; total <= NumberOfParameters; total++) {
      for (int bound = 1; bound <= total; bound++) {
        Write("/// <summary>Binds first ");
        Write(bound.ToString());
        WriteLine(" argument(s) to specified delegate.</summary>");
        WriteLine("/// <returns> A delegate that takes the rest of arguments of original delegate.</returns>");
        Write("public static ");
        WriteType(forAction);
        WriteGenericParameters(bound+1, total, forAction);
        Write(" Bind");
        WriteGenericParameters(1, total, forAction);
        Write("(");
        WriteFirstParameter(1, total, forAction);
        WriteParametersDeclaration(1, bound);
        WriteLine(")");
        WriteLine("{");
        PushIndent("  ");
        Write("return (");
        WriteParameters(bound+1, total);
        Write(") => ");
        Write("d");
        Write(".Invoke(");
        WriteParameters(1, total);
        WriteLine(");");
        PopIndent();
        WriteLine("}");
        WriteLine("");
      }
    }
  };
#>
using System;
using Xtensive.Core;

namespace Xtensive.Core.Helpers
{
  /// <summary>
  /// Extension methods for binding delegates to parameters.
  /// </summary>
  public static class DelegateBindExtensions
  {
<#
PushIndent("    ");
WriteMethods(false);
WriteMethods(true);
PopIndent();
#>
  }
}

Note that int NumberOfParameters = 9 there. That's because we declare additional  Action<...> and Func<...> delegate types in Xtensive.Core. Set it to 4 (or 5 - I don't remember) for .NET 3.5.

P.S. Likely, it's interesting why these methods have appeared. Denis added them while implementing fast expression compilation, later we used them at least in LINQ translator.