Search code examples
c#.netmonoy-combinatoranonymous-recursion

Does "Anonymous Recursion" work in .NET? It does in Mono


I surfed into this site a few days ago on "Anonymous Recursion in C#". The thrust of the article is that the following code will not work in C#:

Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;

The article then goes into some detail about how to use currying and the Y-combinator to get back to "Anonymous Recursion" in C#. This is pretty interesting but a tad complex for my everyday coding I am afraid. At this point at least...

I like to see stuff for myself so I opened the Mono CSharp REPL and entered that line. No errors. So, I entered fib(8);. Much to my great surprise, it worked! The REPL replied back with 21!

I thought maybe this was some magic with the REPL, so I fired-up 'vi', typed in the following program, and compiled it.

using System;

public class Program
{
    public static void Main(string[] args)
    {
        int x = int.Parse(args[0]);
        Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
        Console.WriteLine(fib(x));
    }
}

It built and ran perfectly too!

I am running Mono 2.10 on a Mac. I do not have access to a Windows machine right now so I cannot test this on .NET on Windows.

Has this been fixed on .NET as well or is this a silent feature of Mono? The article is a couple of years old.

If it is Mono only, I cannot wait for the next job interview where they ask me to write a Fibinocci function in the language of my choice (Mono C#) where I have to provide the caveat that .NET will not work. Well, actually I can wait since I love my job. Still, interesting...

Update:

Mono is not really doing "anonymous" recursion as it is using fib as a named delegate. My bad. The fact that the Mono C# compiler assumes a null value for fib before assignment is a bug as noted below. I say "compiler" because the .NET CLR would run the resulting assembly just fine even though the .NET C# compiler would not compile the code.

For all the interview Nazis out there:

Func<int, int> fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;

can be replaced with an iterative version:

Func<int, int> fib = n => 
{
    int old = 1;
    int current = 1;
    int next;

    for (int i = 2; i < n; i++)
    {
        next = current + old;
        old = current;
        current = next;
    }
    return current;
};

You might want to do this because the recursive version is inefficient in a language like C#. Some might suggest using memoization but, since this is still slower than the iterative method, they may just be being wankers. :-)

At this point though, this becomes more of an ad for functional programming than anything else (since the recursive version is so much nicer). It really does not have anything to do with my original question, but some of the answers thought that it was important.


Solution

  • This is a bug in the Mono compiler. It violates section §12.3.3 of the specification. The variable fib cannot be used in the variable initializer, because it isn't definitely assigned.