Don’t use named lexical subroutines

Perl v5.18 allows you to define named subroutines that exist only in the current lexical scope. These act (almost) just like the regular named subroutines that you already know about from Learning Perl , but also like the lexical variables that have limited effect. The problem is that the feature is almost irredeemably broken, which you’ll see at the end of this Item.

How it’s supposed to work

Remember how lexical variables work. You can have package (global) variables and lexical variables with the same name and they do not affect each other.

$cat = 'Buster';
print "Global scope - package cat is $catn";

{
my $cat = 'Mimi';
print "Lexical scope - my cat is $catn";
}

print "Global scope again - package cat is $catn";

Inside the braces, you have a variable with the same name, but it’s a completely different variable. Changing the my variable does not affect the package one:

Global scope - package cat is Buster
Lexical scope - my cat is Mimi
Global scope again - package cat is Buster

Perl v5.6 added the our keyword, which makes a package variable visible in a lexical scope:

$cat = 'Buster';
print "Global scope - package cat is $catn";

OUTER: {
my $cat = 'Mimi';
print "Lexical scope - my cat is $catn";

	INNER: {
		our $cat = 'Roscoe';
		print "INNER lexical scope - our cat is $catn";
		}

print "After INNER scope - my cat is $catn";
}

print "Global scope again - package cat is $catn";

Inside the INNER scope, you change the value of $cat , which is the package version, and you see the change at the end too. Instead of using the my variable in the enclosing scope, you get the global version back:

Global scope - package cat is Buster
Lexical scope - my cat is Mimi
INNER lexical scope - our cat is Roscoe
After INNER scope - my cat is Mimi
Global scope again - package cat is Roscoe

Also remember that the code in a subroutine you define outside of a scope cannot see the my variables:

$cat = 'Buster';
show_cat( '1' );

OUTER: {
my $cat = 'Mimi';
show_cat( '2 (my)' );

	INNER: {
		our $cat = 'Roscoe';
		show_cat( '3 (our)' );
		}

show_cat( '4 (my)' );
}

show_cat( '5' );

sub show_cat {
	print "$_[0]: Cat is $catn";
	}

In the calls to show_cat with the my labels, you still see the global version because that’s the only $cat that the subroutine can see (this is not new):

1: Cat is Buster
2 (my): Cat is Buster
3 (our): Cat is Roscoe
4 (my): Cat is Roscoe
5: Cat is Roscoe

This changes if you define show_cats in the same scope as the lexical variable. If you define it after you declare the my variable, that’s the only version the subroutine sees:

$cat = 'Buster';
show_cat( '1' );

OUTER: {
my $cat = 'Mimi';
sub show_cat {
	print "$_[0]: Cat is $catn";
	}

show_cat( '2 (my)' );

	INNER: {
		our $cat = 'Roscoe';
		show_cat( '3 (our)' );
		}

show_cat( '4 (my)' );
}

show_cat( '5' );

That show_cat bound to the my variable and stuck with that one. This is a closure that allows that lexical variable to sneak out of its scope. That last call takes place after the my variable is gone, even though the subroutine still has a reference to the data:

1: Cat is 
2 (my): Cat is Mimi
3 (our): Cat is Mimi
4 (my): Cat is Mimi
5: Cat is Mimi

You see something different if you define the my variable after you define the named subroutine:

$cat = 'Buster';
show_cat( '1' );

OUTER: {
sub show_cat {
	print "$_[0]: Cat is $catn";
	}
my $cat = 'Mimi';

show_cat( '2 (my)' );

	INNER: {
		our $cat = 'Roscoe';
		show_cat( '3 (our)' );
		}

show_cat( '4 (my)' );
}

show_cat( '5' );

That version only sees the global version of the variable and you get the same output as you saw when you defined the subroutine outside of the scope:

1: Cat is Buster
2 (my): Cat is Buster
3 (our): Cat is Roscoe
4 (my): Cat is Roscoe
5: Cat is Roscoe

If you are comfortable with the variable version of this, you shouldn’t have too much trouble with the subroutine version of it. In Perl, a dynamic language, subroutines are actually data and are also variables. You can change their definitions, and I spend quite a bit of space of Mastering Perl writing about that.

Define two versions of show_cat . Prefix the definition inside the OUTER scope with my even though it’s a named subroutine:

use v5.18;
use feature qw(lexical_subs);
no warnings qw(experimental::lexical_subs);

use vars qw($cat);  # use v5.18 turns on strict

$cat = 'Buster';
show_cat( '1' );

OUTER: {
my sub show_cat {
	print "Inside cat! $_[0] is $catn";
	}
my $cat = 'Mimi';

show_cat( '2 (my)' );

	INNER: {
		our $cat = 'Roscoe';
		show_cat( '3 (our)' );
		}

show_cat( '4 (my)' );
}

show_cat( '5' );

sub show_cat {
	print "Outside cat! $_[0] is $catn";
	}

The output isn’t that different except the middle three calls use the definition of show_cat you defined with my , even though it still looked at the global $cat :

Outside cat! 1 is Buster
Inside cat! 2 (my) is Buster
Inside cat! 3 (our) is Roscoe
Inside cat! 4 (my) is Roscoe
Outside cat! 5 is Roscoe

If you move the lexical subroutine definition after the my $cat , it binds to a different variable just as before:

use v5.18;
use feature qw(lexical_subs);
no warnings qw(experimental::lexical_subs);

use vars qw($cat);  # use v5.18 turns on strict

$cat = 'Buster';
show_cat( '1' );

OUTER: {
my $cat = 'Mimi';
my sub show_cat {
	print "Inside cat! $_[0] is $catn";
	}

show_cat( '2 (my)' );

	INNER: {
		our $cat = 'Roscoe';
		show_cat( '3 (our)' );
		}

show_cat( '4 (my)' );
}

show_cat( '5' );

sub show_cat {
	print "Outside cat! $_[0] is $catn";
	}

Now the inner subroutine know about the my variable, just as it did without the lexical version.

Outside cat! 1 is Buster
Inside cat! 2 (my) is Mimi
Inside cat! 3 (our) is Mimi
Inside cat! 4 (my) is Mimi
Outside cat! 5 is Roscoe

That’s how it works. More importantly, though, is how it doesn’t work and if there’s any new value in this feature.

Problems

Lexical subroutines are experimental, which means they might change or even disappear. It also means they likely have problems. And, from the problems I found, they won’t work the same way in the future if the feature persists.

First, you can’t use a lexical subroutine with sort . This is broken in v5.18 and v5.20 (so far) but is fixed in v5.22.

my sub my_way { ... }

my @sorted = sort my_way @elements;  # looks for main::my_way

This would have been one of the more interesting uses, but it doesn’t really add anything that you can’t already do. If you want a private sort subroutine, a coderef works:

my $my_way = sub { ... }

my @sorted = sort $my_way @elements;  # works

Next, you might immediately think of private methods. It would be quite useful to have a way to do that, but Perl uses the symbol table to resolve methods. An object will not find a lexical subroutine in the symbol table.

So far, I have found only one interesting case where this new feature might provide something that didn’t exist before. The my sub hides a subroutine. An our sub , however, can expose a subroutine in one package to all the other packages in the same lexical scope ( Know what creates a scope ; a package isn’t one of them). If you declare a subroutine with our sub , for the rest of the lexical scope, even across packages, that’s the subroutine you get with that name:

use v5.18;
use feature qw(lexical_subs);
no warnings qw(experimental::lexical_subs);

our sub speak { say "Logging $_[0]" }

package Cat {
	sub meow {
		speak( __PACKAGE__ );
		}
	}

package Dog {
	sub woof {
		speak( __PACKAGE__ );
		}
	}

Cat->meow;
Dog->woof;

The output isn’t surprising:

Logging Cat
Logging Dog

But, what if Cat already has a speak method? In that case, it gets really weird:

use v5.18;
use feature qw(lexical_subs);
no warnings qw(experimental::lexical_subs);

our sub speak { say "Logging $_[0]" }

package Cat {
	sub meow {
		speak( __PACKAGE__ );
		}

	sub speak {
		say "This is a cat speaking!";
		}
	}

package Dog {
	sub woof {
		speak( __PACKAGE__ );
		}
	}

Dog->woof;

speak( "At the end" );

This time, you only call Dog->woof , but you get output from a subroutine that is not in your package nor in your scope:

This is a cat speaking!
This is a cat speaking!

How does Dog know anything about that subroutine? You don’t even get a warning for a redefined subroutine.

You can do the same thing without the named subroutine and it works:

my $speak = sub { say "Logging $_[0]" };

package Cat {
	my $speak = sub { "This is a cat speaking!" };
	sub meow {
		$speak->( __PACKAGE__ );
		}
	}

package Dog {
	sub woof {
		$speak->( __PACKAGE__ );
		}
	}

Dog->woof;

$speak->( "At the end" );

Besides the incredibly bad idea of confusing almost every programmer with the source of a subroutine, but it appears to be insanely broken before v5.22. You might like reading Is it a design flaw that Perl subs aren’t lexically scoped? .

Things to Remember

  • Lexical subroutines are a broken experimental feature you shouldn’t remember.
  • You can already just about everything with a coderef already.
稿源:The Effective Perler (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » Don’t use named lexical subroutines

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录