

Tail calls in F# - clockwork_189
http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx

======
nxn
> For instance, use bindings implicitly generate a try-finally around the code
> that follows them to ensure that the Dispose method is called on the bound
> value. This means that no calls following a use binding will be tail calls.

Reading that just kind of made me break out into a cold sweat. I imagine I
will be looking at some IL tonight because of this.

I still don't quite get the reason for why try-catch or try-finally blocks
cannot have a tail call in them?

~~~
kenjackson
The point of the tail call is to not push an item on the stack frame. That is
why tail call is an optimization you can perform when there are no other
operations after the tail call. You can jump to the next instruction and be
done with it.

But if the tail call is in a try-finally block the "tail call" is not actually
the tail. There is more code that needs to be run after the execution of the
tail call. The code has to return back from the tail call and execute the
finally code.

What you thought was a tail call is actually no longer a tail call at all.

~~~
nxn
I was under the impression that the article stated that a CALL to a recursive
function inside of the try-* block would prevent it from being done with a
tail call, and not that the recursive function contained a wrapping try-*
block in its body. I think I may have understood wrong what the article was
saying.

~~~
kenjackson
_I was under the impression that the article stated that a CALL to a recursive
function inside of the try-_ block would prevent it from being done with a
tail call*

Yes, that statement is correct and what we're talking about.

Having a try-finally in the body of the tail-call function shouldn't make a
difference.

To be clear:

    
    
       try {
          ...
          tailcall(...);
       }
       finally { ... }
    

That could block tail call optimization.

~~~
profquail
According to the .NET CLR spec, you're not allowed to have a tailcall within a
protected ("try") block or it's handler.

On the other hand, tail-calls can be optimized into a while loop, and the F#
compiler does this in some cases (check out the IL it produces), so then you
could have a recursive tail-call within a try/finally there, it just wouldn't
be using the 'tail.call' / 'tail.callvirt' / 'tail.calli' OpCode.

------
abhijitr
Looking at the MSIL emitted by the compiler is not a reliable way to tell that
a tail call will be used at runtime. It's ultimately at the discretion of the
JIT whether to compile the 'tail.callvirt' into an actual tail call in x86.

------
fuzzythinker
Looks like it's a confirmed feature in 2.12 Mono too:

<http://www.mono-project.com/Mono_Project_Roadmap>

~~~
gtani
And you can crank up the stack size (tho this SO has a link to Duffy talking
about increasing stack on the CLR)

[http://stackoverflow.com/questions/7538584/f-vs-ocaml-
stack-...](http://stackoverflow.com/questions/7538584/f-vs-ocaml-stack-
overflow)

