How following recursive lambda call ends/terminates ?
#include <cstdio>
auto terminal = [](auto term) // <---------+
{ // |
return [=] (auto func) // | ???
{ // |
return terminal(func(term)); // >---------+
};
};
auto main() -> int
{
auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; };
auto world =[](auto s){ fprintf(s,"World\n"); return s; };
terminal(stdout)
(hello)
(world) ;
return 0;
}
What am I missing over here ?
The call itself is not recursive. It returns a function object which, if called, will call
terminal
again to generate yet another function object.So
terminal(stdout)
returns a functor which capturesstdout
and can be called with another function object. Calling it again,(hello)
, calls thehello
functor with the captured termstdout
, outputting"Hello"
; the callsterminal
and returns another functor which this time captures the return value ofhello
- which is stillstdout
. Calling that functor,(world)
, just the same again, outputting"World"
.It can be internally translated into something that looks as follows:
LIVE DEMO
Actually this is what the compiler does behind the scene with lambdas (with some approximation).
I think that the source of confusion comes from reading a lambda declaration as a lambda call. Indeed here:
the author just declared a lambda
terminal
which takes one arbitrary argumentterm
and returns an unnamed lambda, nothing more! Let's look at this unnamed lambda, it:func
as argument and calls it on the copy-captured parameterterm
andfunc(term)
; so it returns another unnamed lambda that captures the result offunc(term)
, it but this lambda is not called by now, there is no recursion.Now the trick in the main should be more clear:
terminal(stdout)
returns an unnamed lambda which has captured stdout.(hello)
calls this unnamed lambda passing as arg the hello callable. This gets called on the stdout previously captured.hello(stdout)
returns again stdout which is used as argument of a call to terminal, returning another unnamed lambda which has captured stdout.(world)
same as 2.It's not a recursive function call, look at it step-by-step:
terminal(stdout)
- this simply returns a lambda which has capturedstdout
hello
, which executes the lambda (func(term)
), the result of which is passed toterminal()
, which simply returns a lambda as in 1.world
, which does the same as 2, this time the return value is discarded...terminal(stdout) returns a function, let's call it function
x
, with paramfunc
. So:terminal(stdout) ==> x(func) { return terminal(func(stdout)) };
Now terminal(stdout)(hello) calls function
x(hello)
:terminal(stdout)(hello) ==> x(hello) { return terminal(hello(stdout)) };
This results in
hello
function get called and returns functionx
again.Now terminal(std)(hello)(world) calls function
x(world)
:terminal(stdout)(hello) ==> x(world) { return terminal(world(stdout)) };
This results in
world
function get called and returns functionx
again. Functionx
now is not called any more as there is no more param.The key here is to understand that this is valid:
and will print "Hello World". The recursive series of lambdas can be unrolled as
Coliru example