tags:

views:

392

answers:

13

In light of Michael Carman's comment, I have decided to rewrite the question. Note that 11 comments appear before this edit, and give credence to Michael's observation that I did not write the question in a way that made it clear what I was asking.


Question: What is the standard--or cleanest way--to fake the special status that $a and $b have in regard to strict by simply importing a module?

First of all some setup. The following works:

#!/bin/perl
use strict;
print "\$a=$a\n";
print "\$b=$b\n";

If I add one more line:

print "\$c=$c\n";

I get an error at compile time, which means that none of my dazzling print code gets to run.

If I comment out use strict; it runs fine. Outside of strictures, $a and $b are mainly special in that sort passes the two values to be compared with those names.

my @reverse_order = sort { $b <=> $a } @unsorted;

Thus the main functional difference about $a and $b--even though Perl "knows their names"--is that you'd better know this when you sort, or use some of the functions in List::Util.

It's only when you use strict, that $a and $b become special variables in a whole new way. They are the only variables that strict will pass over without complaining that they are not declared.

: Now, I like strict, but it strikes me that if TIMTOWTDI (There is more than one way to do it) is Rule #1 in Perl, this is not very TIMTOWDI. It says that $a and $b are special and that's it. If you want to use variables you don't have to declare $a and $b are your guys. If you want to have three variables by adding $c, suddenly there's a whole other way to do it.

Nevermind that in manipulating hashes $k and $v might make more sense:

my %starts_upper_1_to_25 
    = skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;`

Now, I use and I like strict. But I just want $k and $v to be visible to skim for the most compact syntax. And I'd like it to be visible simply by

use Hash::Helper qw<skim>;

I'm not asking this question to know how to black-magic it. My "answer" below, should let you know that I know enough Perl to be dangerous. I'm asking if there is a way to make strict accept other variables, or what is the cleanest solution. The answer could well be no. If that's the case, it simply does not seem very TIMTOWTDI.

+1  A: 

$a and $b are just global variables. You can achieve similar effects by simply declaring $k and $v:

use strict;
our ($k, $v);

(In this case $k and $v are not global variables, but lexically scoped aliases for package variables. But if you don't cross the boundaries it's similarly enough.)

moritz
+4  A: 

If I understand correctly, what you want is:

use vars qw($a $b); # Pre-5.6

or

our ($a, $b); # 5.6 +

You can read about it here.

dsm
And the link says "use vars" is obsolete. Besides, I know about use vars and its replacement our. I just don't think it makes strict anymore TMTWOWTDI in practice.
Axeman
Use vars might be obsolete, replaced by 'our', but if you are trying to go for compatibility (which if you're writing a module you probably are), 'use vars' will work more often.
zigdon
perl 5.10 is stable, perl 5.8 is oldstable, and perl 5.6 is dead. If you do anything with Unicode text, trying to work with 5.6 will drive you mad. If somebody wants to use an old perl, he can't expect to have new modules working, IMHO.
moritz
I have edited the response to address your comments. Thanks
dsm
'our' was added in Perl 5.6, not 5.8. 'use vars' is primary useful for backwards compatibility (e.g. if you're releasing a module to CPAN).
Michael Carman
+7  A: 

Others mentioned how to 'use vars' and 'our' - I just wanted to add that $a and $b are special cases, since they're used internally by the sort routines. Here's the note from the strict.pm docs:

Because of their special use by sort(), the variables $a and $b are 
exempted from this check.
zigdon
That's why the question is "Is there some way to make variables like $a and $b in regard to strict?"If there isn't, then it doesn't seem very TMTOWTDI to me.
Axeman
I don't see how explaining why $a and $b are special helps to answer the question of making other variables act the same way. Sorry, -1.
Michael Carman
$a and $b are special, so you can't make other variables like $a and $b. It's compiler magic that makes it work.
brian d foy
brian, I'd appreciate it if you could add a comment to my answer explaining any subtle differences as I don't know enough about the internals.
Michael Carman
+2  A: 

In Perl 5.6 and later, you can use our:

our ($k, $v);

Or you can stick with the older "use vars":

use vars qw($k $v);

Or you might just stick with "my", e.g.:

my %hash;
my ($k,$v);
while (<>) {
  /^KEY=(.*)/ and $k = $1 and next;
  /^VALUE=(.*)/ and $v = $1;
  $hash{$k} = $v;
  print "$k $v\n";
}

__END__
KEY=a
VALUE=1
KEY=b
VALUE=2

Making a global $v is not really necessary in the example above, but hopefully you get the idea ($k on the other hand needs to be scoped outside the while block).

Alternatively, you can use fully qualified variable names:

$main::k="foo";
$main::v="bar";
%main::hash{$k}=$v;
runrig
A: 

EDIT - this is actually incorrect, see the comments. Leaving it here to give other people a chance to learn from my mistake :)


Oh, you're asking if there's a way for a module to declare $k and $v in the CALLER's namespace? You can use Exporter to push up your variables to the caller:

use strict;

package Test; 
use Exporter;

my @ISA = qw/Exporter/; 
my $c = 3; 
my @EXPORT = qw/$c/; 

package main; 
print $c;
zigdon
This doesn't work like you think it does. Take out the Exporter bits and $c is still available in the whole file. A lexical variable doesn't care about package statements and $c is scoped to the file. Similarly, lexical @ISA and @EXPORT are useless to Exporter.
brian d foy
Aside from brian d foy's point, 'use Test' isn't needed because it's in the same file. 'use' reads in *files* not *packages* and, even though file names and package names are generally the same by convention, they are not required to be.
Dave Sherohman
I'm un-accepting this answer on the basis of Brian D. Foy's comment.
Axeman
Fair enough but, even though you know about that, the next person who reads it might not. I've seen a lot of people get confused by that.
Dave Sherohman
+4  A: 

$a and $b are special because they're a part of the core language. While I can see why you might say that the inability to create similarly-special variables of your own is anti-TIMTOWTDI, I would say that it's no more so than the inability to create new basic commands on the order of 'print' or 'sort'. (You can define subs in modules, but that doesn't make them true keywords. It's the equivalent of using 'our $k', which you seem to be saying doesn't make $k enough like $a for you.)

For pushing names into someone else's namespace, this should be a working example of Exporter:

package SpecialK;

use strict;

use base 'Exporter';
BEGIN {
  our @EXPORT = qw( $k );
}

our $k;

1;

Save this to SpecialK.pm and 'use SpecialK' should then make $k available to you. Note that only 'our' variables can be exported, not 'my'.

Dave Sherohman
Except, you can make new basic commands like 'print' and 'sort'. Ones that override 'print' and 'sort', too. :)
Robert P
+1  A: 

It sounds like you want to do the sort of magic that List::MoreUtils does:

use strict;
my @a = (1, 2);
my @b = (3, 4);
my @x = pairwise { $a + $b } @a, @b;

I'd suggest just looking at the pairwise sub in the List::MoreUtils source. It uses some clever symbol table fiddling to inject $a and $b into the caller's namespace and then localize them to just within the sub body. I think.

Eevee
It's a good idea. But I pretty much know how to black-magic my way to most things I want to do. The question asks whether or not their is some "standard" way to do this. I've had my black-magic phase in Perl, I prefer standard ways where available.
Axeman
I'm not aware of any clean wrapper for all this, no. I don't know that it would even be possible; such a module would have to do the localization from the perspective of the *caller*.
Eevee
+1  A: 

This worked for me:

package Special;
use base qw<Exporter>;
# use staging; -> commented out, my module for development
our $c;

our @EXPORT = qw<manip_c>;

sub import { 
    *{caller().'::c'} = *c;
    my $import_sub    = Exporter->can( 'import' );
    goto &$import_sub;
 }

And it passes $c through strict, too.

package main;
use feature 'say';
use strict;
use Special;
use strict;
say "In main: \$c=$c";

manip_c( 'f', sub {
    say "In anon sub: \$c=$c\n"; # In anon sub: $c=f
});

say "In main: \$c=$c";

Yeah, it's kind of dumb that I bracketed my modules with "use strict", but I don't know the internals, and that takes care of potential sequencing issues.

Axeman
I should point out that my answer is basically the same as yours. The primary difference is that I let Exporter handle the dirty work of symbol table munging.
Michael Carman
+1  A: 

Is this what your after?.....

use strict;
use warnings;
use feature qw/say/;

sub hash_baz (&@) {
    my $code   = shift;  
    my $caller = caller;
    my %hash   = (); 
    use vars qw($k $v);

    no strict 'refs';
    local *{ $caller . '::k' } = \my $k;
    local *{ $caller . '::v' } = \my $v;

    while ( @_ ) {
        $k = shift;
        $v = shift;
        $hash{ $k } = $code->() || $v;
    }

    return %hash;
}

my %hash = ( 
    blue_cat   => 'blue', 
    purple_dog => 'purple', 
    ginger_cat => 'ginger', 
    purple_cat => 'purple' );

my %new_hash = hash_baz { uc $v if $k =~ m/purple/ } %hash;

say "@{[ %new_hash ]}";

# =>  purple_dog PURPLE ginger_cat ginger purple_cat PURPLE blue_cat blue

/I3az/

draegtun
A: 

I'm not sure if anyone's clarified this, but strict does not whitelist $a and $b just because they are really convenient variable names for you to use in your own routines. $a and $b have special meaning for the sort operator. This is good from the point of view within such a sort routine, but kind of bad design from outside. :) You shouldn't be using $a and $b in other contexts, if you are.

skiphoppy
+2  A: 

If I'm understanding your question you want to write a module that declares variables in the user's namespace (so they don't have to) and which get localized automatically in callbacks. Is that right?

You can do this by declaring globals and exporting them. (Though do note that it's generally considered bad form to export things without being asked to.)

package Foo;
use strict;
use warnings;

require Exporter;
our @ISA    = qw(Exporter);
our @EXPORT = qw(*k *v hashmap);
our ($k, $v);

sub hashmap(&\%) {
    my $code = shift;
    my $hash = shift;

    while (local ($k, $v) = each %$hash) {
        $code->();
    }
}

Note: The export is of *k and *v, not $k and $v. If you don't export the entire typeglob the local in hashmap won't work correctly from the user's package. A side effect of this is that all of the various forms of k and v (%k, @v, etc.) get declared and aliased. For a full explanation of this, see Symbol Tables in perlmod.

Then in your script:

use Foo; # exports $k and $v

my %h = (a => 1, b => 2, c => 3);

hashmap { print "$k => $v\n" } %h;

__END__
c => 3
a => 1
b => 2
Michael Carman
+1  A: 

$a and $b aren't normal variables, though, and can't be easily replicated by either lexical declarations or explicit exports or messing about with the symbol table. For instance, using the debugger as a shell:

  DB<1> @foo = sort { $b cmp $a } qw(foo bar baz wibble);

  DB<2> x @foo
0  'wibble'
1  'foo'
2  'baz'
3  'bar'
 DB<3> x $a
0  undef
  DB<4> x $b
0  undef

$a and $b only exist within the block passed to sort(), don't exist afterwards, and have scope in such a way that any further calls to sort don't tread on them.

To replicate that, you probably need to start messing about with source filters, to turn your preferred notation

my %starts_upper_1_to_25 
    = skim { $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <= 25 ) } %my_hash
;

into effectively

my %starts_upper_1_to_25
    = map { my $k = $_; my $v = $my_hash{$v};
            $k =~ m/^\p{IsUpper}/ && ( 1 <= $v && $v <=> 25 ) } keys %my_hash
;

$a and $b are as special as $_ and @_, and while there's no easy way to change those names in Perl 5, Perl 6 does indeed fix this, with the given keyword. "given" is a rubbish term to search on, but http://dev.perl.org/perl6/doc/design/syn/S03.html may be a good place to start.

Sam Kington
Your "filtered" example is broken. A hash collapses to a list of interwoven key/value pairs in list context so the $_ in the map will alternate between keys and values, not just keys.
Michael Carman
A: 

The modules suggested that use export are really no different from use vars. But the use vars would need to be done in each package that used the $a-like variable. And our() would need to be done in each outer scope.

Note that you can avoid using $a and $b even for sort by using a $$-prototyped sub:

sub lccmp($$) { lc($_[0]) cmp lc($_[1]) }
print join ' ', sort lccmp
   qw/I met this guy and he looked like he might have been a hat-check clerk/;

This is essential when using a compare routine in a different package than the sort call.

ysth