Functional C# Revisited - Into the Great Void

Lately, I've been doing some functional C# in both user groups and on this blog.  As the C# language has evolved it has definitely taken some functional programming aspects, such as high order functions, extension methods, LINQ and so on.  But with that, there is a cost.  Functional programming with C# tends to be verbose due to a number of things which include the lack of type inference on methods, void as not a first class citizen and so on.  I'm going to explore a couple of those today in this post.

Into the Great Void

I conversed with Jimmy Bogard last week regarding some limitations I saw in the C# language and how F# is better suited to handling this issue.  One thing that has frustrated me is the fact that the System.Void structure is not treated as a first class citizen, in that I cannot do the following:

Func<int, int, Void> equals = (x, y) => Assert.Equal(x, y);

I get the following error if I even try to do this:

System.Void cannot be used from C# -- use typeof(void) to get the void type object

And why is that exactly?  The ECMA Standard 335, Partition II, Section 9.4 "Instantiating generic types" states:

The following kinds of type cannot be used as arguments in instantiations (of generic types or methods):
  • Byref types (e.g., System.Generic.Collection.List`1<string&> is invalid)
  • Value types that contain fields that can point into the CIL evaluation stack (e.g.,List<System.RuntimeArgumentHandle>)
  • void (e.g., List<System.Void> is invalid)
To me, this is a big issue.  Languages such as F# do allow for such actions which allows us to have a void (unit) function declared the same way as we would with a function that returns a value.  What it translates to is Microsoft.FSharp.Core.Unit type which typically hides the value.  From my earlier example of the functional unit testing in F#, I could declare:

let should f actual = f actual

let not_throw_exception actual =
  Assert.DoesNotThrow(Assert.ThrowsDelegate(actual))

which compiles down to:

public static U should<T, U>(FastFunc<T, U> f, T actual)

public static void not_throw_exception(FastFunc<Unit, Unit> actual)

What this allows me to do is pass in a function that will return nothing, and yet, if I passed in a FastFunc<int, int> that would work too with the should function.  Both of them are treated the same, yet not in C#.  Instead, we are forced to differentiate

Func versus Action?

As we're forced to differentiate our functions, it makes it hard to generalize functional programming in C#.  I feel at this point, C# can't be a first class functional language because we need to make this distinction.  If we don't return a value, we must use the Action<T> whereas if we do return a value, we must use Func<TArg, TResult>.  Let's look at some samples where we have to differentiate.

Let's first look at the forward operator function in C#.  This is one we looked at last time:

F# example

let (|>) x f = f x
[1..10] |> List.map(fun x -> x * x)

C# version

public static TResult ForwardFunc<TArg1, TResult>(this TArg1 arg1, Func<TArg1, TResult> func)
{
    return func(arg1);
}

- And -

public static TResult ForwardFunc<TArg1, TArg2, TResult>(this TArg1 arg1, Func<TArg1, TArg2, TResult> func, TArg2 arg2)
{
    return func(arg1, arg2);
}

This allows me to do the following code:

var mapResult = Enumerable.Range(1, 10).ForwardFunc(x => x.Map(i => i * i));
var mulResult = 3.ForwardFunc((x, y) => x * y, 3);

Whereas if my methods returned void, I'd also have to create functions to match that as well, such as:

public static void ForwardAction<TArg1>(this TArg1 arg1, Action<TArg1> func)
{
    func(arg1);
}

- And -

public static void ForwardAction<TArg1, TArg2>(this TArg1 arg1, Action<TArg1, TArg2> func, TArg2 arg2)
{
    func(arg1, arg2);
}

And then I can accomplish this code:

true.ForwardAction(x => x.ShouldBeTrue());
Enumerable.Range(1, 10).ForwardAction((x, y) => x.ShouldNotContain(y), 3);

These of course are simplistic examples, and it just shows that you have to think a little bit about whether you return something or not.  Not something you have to necessarily think about in F# or other functional languages.  Currying is also pretty difficult using the Action delegate as well.  It's not really a usable thing at this point.  Feel free to correct me, however...

How could you curry an Action given that you curry any normal function such as this?

public static Func<TArg1, Func<TArg2, TResult>> Curry<TArg1, TArg2, TResult>(this Func<TArg1, TArg2, TResult> f)
 {
    return a1 => a2 => f(a1, a2);
}

Maybe something like this?

public static Action<TArg2> Curry<TArg1, TArg2>(this Action<TArg1, TArg2> func, TArg1 arg1)
{
    return a2 => func(arg1, a2);
}

But of course this function can't scale as you add more parameters to this.  So, this isn't really an ideal situation.  Unless I'm missing something blindingly obvious.

Wrapping It Up

With these given limitations of the void type, lack of type inference on method signatures, etc, it's hard to take C# seriously as a full citizen in the functional programming sense.  I think it's a rather large weakness to me.  Instead, I think we should focus on languages which already make these semantics easy, such as Haskell, F# and so on for when you need functional programming.  Sure, C# can support a lot of functional programming paradigms, but it doesn't quite feel natural.

kick it on DotNetKicks.com

5 Comments

  • Hey Matthew, I completely agree. Doing "real" currying in F# (or other functional language) is more more intuitive than C#. But I do like that C# is at least letting me introduce the functional concepts on my projects in language that all team members know. I can ease the functional concepts in with C# without them having to learn a completely new syntax.

    So I will plod along with what C# allows me to do, forever whispering that another .NET language (F#) is much more succinct and natural.

  • Instead of Func, try Action.

  • Sorry, I missed the last half of your post where you discussed Action before posting my previous comment. Please ignore it.

  • Could you give an example of why you would want to curry actions, which, to me, doesn't seem like something you could curry? Also, explain how you would do it in F#. Thanks!

  • To curry Actions, do

    public static Func<TArg1, Action> Curry(this Action a) {return x1 => (x2 => {a(x1, x2);});}

Comments have been disabled for this content.