Using closures with "use strict"

B

Ben Morrow

Quoth ddtl said:
If I define a "use strict" and want to create a closure,
how can I refer to the anonymous functions defined in that
closure? Consider the following example:

-------------------------------------------
use strict;

{
our $f = sub {print "Hi\n";};
}

&$f;
-------------------------------------------

Here compiler complains, and reports an error:

"Variable "$f" is not imported at ~/test line 7."

The variable declared by 'our' is a lexically-scoped alias to the
package global, so you need to put it in the same place as you would put
an equivalent 'my' variable. What you want is

use strict;

our $f;
{
# presumably there is more code here?

$f = sub {...};
}

# I prefer this style of indirect function call, as it matches the other
# deref ops and doesn't lead to potential confusion about calling named
# subs with &.

$f->();

or perhaps

our $f = do {
...
sub {...};
};

Ben
 
D

David K. Wall

If I define a "use strict" and want to create a closure,
how can I refer to the anonymous functions defined in that
closure? Consider the following example:

-------------------------------------------
use strict;

{
our $f = sub {print "Hi\n";};
}

&$f;
-------------------------------------------

That doesn't fit my idea of a closure. It's just a code reference.
As you noted, the code above is broken because variables declared
with our() are lexically scoped.

A closure (as I understand it) is a subroutine that keeps track of
lexical variables that are within scope when that subroutine is
defined, so that later it can still access those variables even
though they're out of the present scope.

For example:

use strict;

my $f;
{
my $string = 'test';
sub set_string { $string = $_[0] }
sub get_string { $string }
$f = \&get_string;
}

print get_string(), "\n";
set_string('something else');
print get_string(), "\n";

# call get_string() using a code reference
print $f->(), "\n";

# this statement will generate an error, because $string is
# now out of scope
print "\$string = $string\n";


set_string() and get_string() are closures. $f is just a code
reference to what happens to be a closure.
 
D

ddtl

Hello,

If I define a "use strict" and want to create a closure,
how can I refer to the anonymous functions defined in that
closure? Consider the following example:

-------------------------------------------
use strict;

{
our $f = sub {print "Hi\n";};
}

&$f;
-------------------------------------------

Here compiler complains, and reports an error:

"Variable "$f" is not imported at ~/test line 7."

Obviously, though 'f' is declared using 'our' modifier, as there
is no such a global variable called 'f', using 'our $f;' creates
some kind of restricted global variable (don't know even how to
call it - that variable works like a global one, but it's "globalness"
is restricted to the enclosing block).

In order to rectify it (and still have the benefits of "use strict",
there are two possibilities that I can see:

1) Enclose closure and every call to anonymous function defined in
the closure in "no strict":

------------------------------------
use strict;

{
no strict;
$f = sub {print "Hi\n";};
use strict;

}

#...
#...

no strict;
&$f;
use strict;
#...
#...
------------------------------------

2) Define all the references to anonymous functions in closure as
"our $REF_NAME" somewhere outside the block:


------------------------------------
use strict;

{
our $f = sub {print "Hi\n";};
}
our $f;

&$f;
------------------------------------


Another solution can maybe involve some tricks with package declarations,
but I am only in the "Chapter 8" of "Programming Perl", so I can't
be sure.


The problem is, that all those solutions are looking very contrived
and ugly.

Is there is a better way to do that?


ddtl.
 
M

Malcolm Dew-Jones

ddtl (this.is@invalid) wrote:

: Hello,

: If I define a "use strict" and want to create a closure,
: how can I refer to the anonymous functions defined in that
: closure? Consider the following example:

: -------------------------------------------
: use strict;

: {
: our $f = sub {print "Hi\n";};
: }

: &$f;
: -------------------------------------------

: Here compiler complains, and reports an error:

: "Variable "$f" is not imported at ~/test line 7."

: Obviously, though 'f' is declared using 'our' modifier, as there
: is no such a global variable called 'f', using 'our $f;' creates
: some kind of restricted global variable (don't know even how to
: call it - that variable works like a global one, but it's "globalness"
: is restricted to the enclosing block).
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Yes (sort of), that's what "our" does. It tells the compiler you intend
to access the global variable $f, but only within the current scope.

If you intend to access the same global variable within a different scope
then it must also be defined within that different scope as well.

There are various things you could do. (most examples untested)

#1
use strict;
my $f;
{
$f = sub {print "Hi\n";};
}
&$f;

#2
use strict;
our $f;
{
$f = sub {print "Hi\n";};
}
&$f;

#3
use strict;
{
our $f = sub {print "Hi\n";};
}
our $f;
&$f;
#4
use strict;
{
our $f = sub {print "Hi\n";};
}
{
our $f;
&$f;
}

#5
use strict;
yse vars '$f';
{
$f = sub {print "Hi\n";};
}
&$f;

and possibly others. Unless $f must be accessed by routines in different
files then the #1 (my $f) is probably best. However, "our" does have the
advantage that you can declare it within smaller blocks as needed (#4),
which makes it available to less than the entire file (unlike my, where
everything after the declaration must see the variable if any of the code
is to see it).
 
T

Tad McClellan

ddtl said:
If I define a "use strict" and want to create a closure,
-------------------------------------------
use strict;

{
our $f = sub {print "Hi\n";};
}

&$f;
-------------------------------------------


There is no closure there you know.

Maybe your question is how to make a closure or what constitutes
a "closure"?

Obviously, though 'f' is declared using 'our' modifier, as there
is no such a global variable called 'f', using 'our $f;' creates
some kind of restricted global variable (don't know even how to
call it - that variable works like a global one, but it's "globalness"
is restricted to the enclosing block).


I think you are confusing the scope of the _name_ of the variable
with being able to access the value of the variable.

You can always access package variables (so they are "global").

All our() does for you is give you temporary ('til end of block)
permission to use short names instead of fully qualified names.

with our($x):

print $x;


without our($x):

print $main::x;

or

print $Cool::package::x;


You can access the value at any place in the code, its value is global.

You can access the value by the "short name" only in the our()'s scope.

Is there is a better way to do that?


I think the other followups have already done that part.
 
D

ddtl

That doesn't fit my idea of a closure. It's just a code reference.
As you noted, the code above is broken because variables declared
with our() are lexically scoped.

A closure (as I understand it) is a subroutine that keeps track of
lexical variables that are within scope when that subroutine is
defined, so that later it can still access those variables even
though they're out of the present scope.

You are right of course - this is not actually a closure, but was just too
lazy to type the whole ~10 more characters to provide anonymous sub
with it's "personal" scoped variables :). Anyway, the question was clearly
understood by everybody, despite of my lazyness.

ddtl.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top