Reputation: 6771
Lua claims that it implement tail call properly thus no stack needs to be maintained for each call thus allow infinite recursion, I tried to write a sum function, one is not tail call, and one is tail call:
non tailcall version
function sum(n)
if n > 0 then
return n + sum(n-1)
end
end
print(sum(1000000))
stackoverflow as expected.
tailcall version
function sum2(accu, n)
if n > 0 then
accu.value = accu.value + n
sum2(accu, n-1)
end
end
local accu = {value = 0}
sum2(accu, 1000000)
print(accu.value)
I suppose there would be no stackoverflow in this case as it is a tailcall, but it still failed due to stackoverflow:
/bin/lua/5.1.4/bin/lua: tailcall.lua:13: stack overflow
stack traceback:
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
...
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:13: in function 'sum2'
tailcall.lua:17: in main chunk
[C]: ?
So is lua really support tail call optimization, or my function is actually not tail call here?
I am using lua 5.1.4 on redhat 5
Upvotes: 15
Views: 5682
Reputation: 63
As has already been said, the only correct tail call syntax is return f(...)
That's because f() ; return
is not the same as return f()
: the former requires a final operation after calling f
: drop its results to ensure nothing is returned.
but how could lua can't figure out this is actually a tail call
Because lua is a very "dynamic" language: in
function sum2(accu, n)
if n > 0 then
accu.value = accu.value + n
sum2(accu, n-1)
end
end
sum2
is not calling itself, it's calling the global variable "sum2"
(or equivalent upvalue if declared local
)
And this variable could become a function returning values, in which case emitting a tail call bytecode would be incorrect.
In other words: whether f
makes a tail call is knowledge that is inaccessible to the compiler, and will only be confirmed at runtime, unless you use the unbreakable syntax return f(...)
Upvotes: 1
Reputation: 6858
Tail call in Lua must have the following form
return fct(args)
So your tail call version must be rewritten as:
function sum2(accu, n)
if n > 0 then
accu.value = accu.value + n
return sum2(accu, n-1) --< note the return here
end
end
Upvotes: 23