In this node dragonchild states :
But, you're working with global variables that don't even have the safety of the compiler making sure things are ok. Hence, mispellings aren't caught by strict 'vars', etc.

But run this program:

package Mr::Rogers; use strict; __PACKAGE__->{age} = 20; __PACKGE__->{age} = 20; 1;

And note that the spelling error is caught at compile time, via perl -cw $filename, so the while the statement about symrefs is right in general for this special package hash it is not applicable and hence this remains a viable programming technique.

Replies are listed 'Best First'.
Re: encapsulation via compiler-checked symrefs
by demerphq (Chancellor) on Nov 05, 2001 at 18:52 UTC
    Well actually, I think if you play around with this a bit more you will realize that what you are saying _only_ applies to the magic __PACKAGE__ identifier. This wont work with any other string, nor if you evaluate __PACKAGE__ into a string.
    package Mr::Rogers; use strict; my $r=__PACKAGE__; __PACAKGE__->{height}=200; #this is ok under strict for some reason $r->{age}=20; #this will die under strict;
    Personally this is not a technique that I would use except in extremely unusual circumstances (obfu, golf etc) and would convert any code using such shenanigans to use real references before use, either that or not use it at all.

    Update
    Something about this was bothering me, specifically which package was having its symbol table raped. With a bit of help from broquaint I figured it out, and it just makes me all the more against the very idea of this type of construct.

    What exactly do you think __PACKAGE__->{age} in the above example is refering to? As in what hash in what package? I bet not what you think it is.

    The answer of course is that it is creating a variable name 'Rogers' in the package named 'Mr'. While this may be what you thought and what you wanted it is very misleading. Anyone seeing the __PACKAGE__ token would expect things to be happening in the current package, but of course this is not so.

    The reminds me of a little demonstration of naming implications. Get a bunch of differently coloured pens. Line them up and then write the name of the second pens color with the first pen, and so on for the rest, warping around for the last pen. Now try to recite the colors of each word quickly, over and over. Bet you make a mistake in first few words. Its even harder when they really are messed up.

    Purple Yellow Blue Pink Red Green

    Yves / DeMerphq
    --
    You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

Gods, don't try to justify using symrefs!
by dragonchild (Archbishop) on Nov 05, 2001 at 20:41 UTC
    Actually, '__PACKAGE__' and '__LINE__' (and a few others) are special compiler directives. (They're not exactly directives, but close enough.) They're NOT variables, hence they don't fall under strict's purview, nor any other purview, for that matter.

    The reason the spelling error is caught is because __PACKGE__ isn't a compiler directive in the way __PACKAGE__ is, hence the compiler doesn't know what to do about it.

    Symbolic references are a valid programing technique. They let you do stuff that can't be done any other way, and that's one of Perl's selling points. I use them in an alternate way of implementing Perl OO.

    But, do not use them unless you know why you shouldn't use them! They are an extremely dangerous technique and will get you into trouble. Unless they are used sparingly, you will not be able to maintain that code, nor will anyone else. You will not be able to prove your code works, espeically not after the first 10 bugfixes and enhancements. I don't care how much documentation you have - it won't be enough to cover all the cases.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Actually, '__PACKAGE__' and '__LINE__' [...] are special compiler directives. [...] They're NOT variables, hence they don't fall under strict's purview, nor any other purview, for that matter.

      __PACKAGE__ doesn't bypass strict because it is some "special" "non-variable". It gets past strict because it is a compile-time constant (as I discussed in the thread that introduced this topic, self-referent hash for your packages).

      Some more examples:

      #!/usr/bin/perl -w my @strictures; BEGIN { require strict; if( ! grep /-v/, @ARGV ) { @strictures= "refs"; } } use strict @strictures; sub name1() { return "Foo::Bar" } sub name2 { return "Foo::Bar" } "Foo::Bar"->{key}= "value"; # No error print name1->{key},$/; # No error, prints "value" eval { print name2->{key},$/ # Fatal run-time error # Can't use string ("Foo::Bar") as a HASH ref while "strict refs" } or warn $@; { package Foo::Bar; print __PACKAGE__->{key},$/; # No error, prints "value" } eval q{ sub name3() { return "Foo" } sub name4 { return "Foo" } "Foo"->{key}= "bar"; # If only strict 'refs', then prints "bar" w/ no errors # (-v)Global symbol "%Foo" requires explicit package name print name3->{key},$/; # If only strict 'refs', then prints "bar" w/ no errors # (-v)Global symbol "%Foo" requires explicit package name eval { print name4->{key},$/; # Can't use string ("Foo") as a HASH ref while "strict refs" } or warn "Line ",__LINE__,": $@"; eval q{ package Foo; print __PACKAGE__->{key},$/; # Use of uninitialized value in print 1; } or warn "Line ",__LINE__,": $@"; } or warn "Line ",__LINE__,": $@";
      Which boils down to some interesting things:

      • Compile-time string contants that contain "::" can be used as symbolic references even under use strict "refs".
      • Compile-time string contants that don't contain "::" generate a fatal "Global symbol requires explicit package" error under use strict "vars".
      • The (horrid) __PACKAGE__-> trick, when used in a package whose name contains no "::", fails under use strict "vars" and tries to access a hash whose name is __PACKAGE__."::".__PACKAGE__ (instead of the usual name of __PACKAGE__).

      Which makes me swing more toward the opinion that compile-time constant strings getting past use strict "refs" is simply a bug (and should put one more nail in the coffin of this hack).

      Update: BTW, the reason that compile-time string constants get past use strict "refs", is (as I suspected) because the resulting code gets compiled as if the string were entered "bare":

      perl -MO=Deparse -e "'Foo'->{key}= 'value'" $Foo{'key'} = 'value'; -e syntax OK

              - tye (but my friends call me "Tye")