Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

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.

This entry was posted in C#, F#, Functional Programming. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://podwysocki.codebetter.com Matthew.Podwysocki

    @Justin

    I gave the same advice as well when I did the presentation. You can improve your code by losing the foreaches, making your code more intent driven (high order functions, maps, filters, partial application and so on), but ultimately if you need a functional language, then F# will be your choice on the .NET platform.

    I agree that C# shouldn’t suddenly try to become the next FP language, nor the next dynamic language, but instead, stay the mainstream OOP language that it is with functional programming aspects to it.

    Matt

  • http://podwysocki.codebetter.com Matthew Podwysocki

    @Corey

    Don’t get me wrong, I do like the fact that you can do a lot of interesting FP things with C#, but if you’re looking for a complete FP solution, F# is your answer. I don’t think C# will get to that level of conciseness that F# will provide, nor should it. Instead, it serves as a perfectly valid FP bridge.

    Matt

  • http://www.codethinked.com Justin Etheredge

    I recently gave a talk at my local user group on function features in C# and I tried to make this argument very explicit. I told them that if they wanted to do functional programming, then they needed to go check out F# or the like. I was merely there to show them some of the techniques and features that C# has borrowed from functional programming and let them see how using some of those techniques can make their code better.
    I think it would be a mistake though to try and turn C# into a functional language, just as it would be a mistake to try and turn it into a dynamic language. This certainly doesn’t mean that it can’t learn lessons from other paradigms though.

  • http://www.coreyhaines.com Corey Haines

    good points. While C# can’t be considered a first-class functional language, it is important not to minimize the benefits one can get by following some good functional principles when developing code. You may not be able to do functional nearly as well in C# as you can in a language created for it, such as F#, but you can reap a lot of the benefits.