Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of the extra ldnull and tail. in F# implementation vs C#?

The following C# function:

T ResultOfFunc<T>(Func<T> f)
{
    return f();
}

compiles unsurprisingly to this:

IL_0000:  ldarg.1     
IL_0001:  callvirt    05 00 00 0A 
IL_0006:  ret  

But the equivalent F# function:

let resultOfFunc func = func()

compiles to this:

IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  tail.       
IL_0005:  callvirt    04 00 00 0A 
IL_000A:  ret 

(Both are in release mode). There's an extra nop at the beginning which I'm not too curious about, but the interesting thing is the extra ldnull and tail. instructions.

My guess (probably wrong) is that ldnull is necessary in case the function is void so it still returns something (unit), but that doesn't explain what is the purpose of the tail. instruction. And what happens if the function does push something on the stack, isn't it stuck with an extra null that doesn't get popped?

like image 426
Asik Avatar asked May 29 '15 22:05

Asik


People also ask

Why is an F-test always one-tailed?

The reason is that t2 has no negative values. In a F-distribution, all differences are values in the same direction. It's like when you close a book that was open: Where all pages were on both sides of the spine in the open book (the t-test) all pages are on one side of the spine in a F-test.

How many tails is an F-test?

An F-test (Snedecor and Cochran, 1983) is used to test if the variances of two populations are equal. This test can be a two-tailed test or a one-tailed test. The two-tailed version tests against the alternative that the variances are not equal.

Does F distribution have two tails?

Symmetrical distributions like the t and z distributions have two tails. Asymmetrical distributions like the F and chi-square distributions have only one tail.

What is the purpose of F value?

The F value is used in analysis of variance (ANOVA). It is calculated by dividing two mean squares. This calculation determines the ratio of explained variance to unexplained variance. The F distribution is a theoretical distribution.


1 Answers

The C# and F# versions have an important distinction: C# function doesn't have any parameters, but F# version has one parameter of type unit. That unit value is what shows up as ldnull (because null is being used as representation of the only unit value, ()).

If you were to translate the second function to C#, it would look like this:

T ResultOfFunc<T>( Func<Unit, T> f ) {
   return f( null );
}

As for the .tail instruction - that is so called "tail call optimization".
During a regular function call, a return address gets pushed on the stack (the CPU stack) and then the function is called. When the function is done, it executes the "return" instruction, which pops the return address off the stack and transfers control there.
However, when function A calls function B, and then immediately returns function B's return value, without doing anything else, the CPU can skip pushing the extra return address on the stack, and perform a "jump" to B instead of a "call". That way, when B executes the "return" instruction, the CPU will pop return address from the stack, and that address will point not to A, but to whoever called A in the first place.
Another way to think about it is: function A calls function B not before returning, but instead of returning, and thus delegates the honor of returning to B.

So in effect, this magic technique allows us to make a call without consuming a spot on the stack, which means that you can perform arbitrarily many such calls without risking stack overflow. This is very important in functional programming, because it allows to efficiently implement recursive algorithms.

It is called "tail call", because call to B happens, so to say, at the tail of A.

like image 142
Fyodor Soikin Avatar answered Nov 15 '22 16:11

Fyodor Soikin