Search code examples
f#tail-recursiontail-call-optimizationtail-call

F# tail call broken by lack of parentheses


While testing F# tail calls with F# team blog article I found that almost the same code has the same result but different IL although only parentheses in the code differ.

Next code is optimized by compiler and I see br.s IL_0000 in the end of IL and no calls to sumSoFar

let rec loopAndSum aList sumSoFar = 
    match aList with
    | [] -> sumSoFar
    | x :: xs -> 
        loopAndSum xs (sumSoFar + x)

loopAndSum [ 1..5 ] 0 
|> printfn "sum: %i"

But that piece is not optimized by compiler and it has call bla_bla.loopAndSum near to the end of IL.

let rec loopAndSum aList sumSoFar = 
    match aList with
    | [] -> sumSoFar
    | x :: xs -> 
        loopAndSum xs sumSoFar + x

loopAndSum [ 1..5 ] 0 
|> printfn "sum: %i"

These examples differ only with parentheses around sumSoFar + x. You can play with it and look at IL at .NET Fiddle.

Does anybody know why parentheses matter?


Solution

  • Function application has higher precedence than any operator. So without the parentheses, it is equivalent to:

    (loopAndSum xs sumSoFar) + x
    

    Therefore it is not a tail call: the addition is performed after the recursive call. It is just by chance that the result value is correct.