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
and earthlings
get appended to the words howdy
and greetings
.
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.
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
$f = \make_saying("Howdy")
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
my $x;
sub foo {
my $y;
return sub { "$x, $y" };
}
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 to Howdy
. The returned closure remembers this scope.
When we execute it again with make_saying("Greetings")
, the body of make_saying
is evaluated again. The $salute
is now set to Greetings
, 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.
If a variable is asigned to a function, is it automatically a
reference to that function?
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).
In that above code, can i write $f = \make_saying("Howdy") instead?
And when can i use the & cause i tried using that in passing the
parameters (&$f("world")) but it doesnt work.
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:
my $f = \&make_saying;
my $other_f = $f->("Howdy");
$other_f->("world");
and lastly, In that code above how in he** did the words world and
earthlings got appended to the words howdy and greetings.
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).
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";'