Newbie in Perl again here, trying to understand closure
in Perl.
So here's an example of code which I don't understand:
sub make_saying {
my $salute = shift;
my $newfunc = sub {
my $target = shift;
print "$salute, $target!\n";
};
return $newfunc; # Return a closure
}
$f = make_saying("Howdy"); # Create a closure
$g = make_saying("Greetings"); # Create another closure
# Time passes...
$f->("world");
$g->("earthlings");
So my questions are:
- If a variable is assigned to a function, is it automatically a reference to that function?
- In that above code, can I write
$f = \make_saying("Howdy")
instead? And when can I use the&
because I tried using that in passing the parameters (&$f("world")
) but it doesn't work. - and lastly, In that code above how in he** did the words
world
andearthlings
get appended to the wordshowdy
andgreetings
.
Note: I understand that $f is somewhat bound to the function with the parameter howdy
so that's my understanding how the world
got appended. What I don't understand is the 2nd function inside. How that one operates its magic. Sorry I really don't know how to ask this one.
Every time you call the subroutine 'make_saying', it: 1a - create a DIFFERENT closure 2a - assign the received parameter to the scalar '$salute' 3a - declare (create but not execute) the inner anonymous subroutine: that the reason why at this moment nothing is assigned to the scalar '$target' nor is executed the statement 'print "$salute, $target!\n";' 4a - finally the subroutine 'make_saying' return a reference to the inner anonymous subroutine, this reference become the only way to call the (specific) anonymous subroutine.
Ever time you call each anonymous subroutine, it: 1b - assign the received parameter to the scalar '$target' 2b - see also the scalar '$salute' that will have the value assigned at the moment in which was created the anonymous subroutine (when was called its parent subroutine 'make_saying' 3b - finally execute the statement 'print "$salute, $target!\n";'
In Perl, scalar variables cannot hold subroutines directly, they can only hold references. This is very much like scalars cannot hold arrays or hashes, only arrayrefs or hashrefs.
The
sub { ... }
evaluates to a coderef, so you can directly assign it to a scalar variable. If you want to assign a named function (e.g.foo
), you have to obtain the reference like\&foo
.You can call coderefs like
$code->(@args)
or&$code(@args)
.The code
evaluates
make_saying("Howdy")
, and takes a reference to the returned value. So you get a reference that points to a coderef, not a coderef itself.Therefore, it can't be called like
&$f("world")
, you need to dereference one extra level:&$$f("world")
.A closure is a function that is bound to a certain environment.
The environment consists of all currently visible variables, so a closure always remembers this scope. In the code
foo
is a closure over$x
, as the outer environment consists of$x
. The inner sub is a closure over$x
and$y
.Each time
foo
is executed, we get a new$y
and therefore a new closure. Each time it is called, a different closure is returned.When we execute
make_saying("Howdy")
, the$salute
variable is set toHowdy
. The returned closure remembers this scope.When we execute it again with
make_saying("Greetings")
, the body ofmake_saying
is evaluated again. The$salute
is now set toGreetings
, and the inner sub closes over this variable. This variable is separate from the previous$salute
, which still exists, but isn't accessible except through the first closure.The two greeters have closed over separate
$salute
variables. When they are executed, their respective$salute
is still in scope, and they can access and modify the value.No. In example the function
make_saying
return reference another function. Such closures do not have name and can catch a variable from outside its scope (variable$salute
in your example).No.
$f = \make_saying("Howdy")
is not what you think (read amon post for details). You can write$f = \&make_saying;
which means "put into $f reference to function make_saying". You can use it later like this:make_saying creating my variable which goes into lamda (
my $newfunc = sub
); that lambda is returned from make_saying. It holds the given word "Howdy" through "closing" (? sorry dont know which word in english).