Comparing Continuations in C# and F# Part 2

In my last post I went over the differences between using a continuation in F# and C#.  As it turns out I was right about the limits and symptoms but wrong about the reason. 

The F# code does indeed generate tail calls for part of the continuation.  However this is only a very small portion of the actual code and is in fact only generated for the call in the empty case.  I misread this function to be the call for the overall continuation.  Instead it is the function for the entire “inner” lambda.

So why does F# perform differently than C# in this scenario?

Andrew Kennedy pointed out that F# will actually transform the “inner” function into a loop.  In affect the code generated looks like the following.

     TypeFunc func = this._self3;
    while (true)
    {
        if (!this.e.MoveNext())
        {
            break;
        }
        A cur = this.e.Current;
        cont = new Program.clo@9<U V, A ,>(this.combine, cont, cur);
    }
    return cont.Invoke(this.acc);

The actual transformation into a loop is what is preventing F# from overflowing the stack here.  Iteration incurs no stack overhead in this case. 

Even more interesting is that the tail opcode is quite simply ignored when dealing with un-trusted code.  It therefore cannot be relied on to generate performant code in all scenarios.