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?
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.