Ovid has asked for the wisdom of the Perl Monks concerning the following question:

In Re: Module Naming Dilemma, Anonymous Monk asked why I wasn't simply aliasing typeglobs together for some code I was working on. Ignoring the fact that I wanted to minimize that behavior, I was kind of stumped by the following code snippet:

#!/usr/local/bin/perl -lw use strict; package Foo; sub new { print "Expected class: $_[0]"; bless {} => shift } sub test { print "Actual class: ". ref shift } package main; *Bar:: = \*Foo::; Bar->new->test; Foo::new('Bar')->test;

Both times that test() is called, I would expect ref to return Bar but it instead returns Foo. Why is that?

I (once again) cannot find where this behavior is documented, even though I suspect it is. Frankly, this code does not do what I would expect it to to do.

Cheers,
Ovid

New address of my CGI Course.

Replies are listed 'Best First'.
Re: Two argument bless sometimes ignores the class name?
by bart (Canon) on Jan 07, 2005 at 13:27 UTC
    It doesn't ignore the class name.

    Instead, it appears to me by overwriting the namespace of Bar, you're overwriting everything in Bar::, including it's "identity". So class Bar is from now on, identified as class Foo.

    Check out this similar, but even more reduced code:

    { package Foo; sub foo { bless {}, "Bar" } } *Bar:: = \*Foo::; use Data::Dumper; print Dumper(Foo->foo);
    There's no reason why it wouldn't print "Bar" for the class, no? Yet, this is the result I get:
    $VAR1 = bless( {}, 'Foo' );

    Drop the assignment of the stash, and it behaves as you and I expect.

    I have no idea where the identity of the class is stored in the stash, but it must be somewhere — and you're overwriting it.

    update: Replace the assignment of the stash with the less rigorous:

    *Bar::foo = \*Foo::foo;
    and it still behaves as expected, yielding:
    $VAR1 = bless( {}, 'Bar' );
    whether you call it like either of:
    print Dumper(Foo->foo); print Dumper(Bar->foo);

      This has got to be a bug and quite possibly the most bizarre behavior I've ever seen from perl.

Re: Two argument bless sometimes ignores the class name?
by stvn (Monsignor) on Jan 07, 2005 at 05:09 UTC
    Frankly, this code does not do what I would expect it to to do.

    Maybe its just getting late, but I am not sure I see why this is odd. You are not duplicating the contents of the TYPEGLOB, but just aliasing it. It's just a level of indirection, not a copy of the package. In fact, just changing one line to this:

    %Bar:: = %Foo::;
    Gives me this output
    Expected class: Bar Actual class: Bar Expected class: Bar Actual class: Bar
    Which is exactly what I would expect. And while I do agree the behavior you describe is not very clear, I am not sure I would have expected it to work without issue.

    -stvn
Re: Two argument bless sometimes ignores the class name?
by Errto (Vicar) on Jan 07, 2005 at 05:10 UTC

    Well, here's something related I don't quite get that might help. I modified your code somewhat:

    use strict; package Foo; sub new { print "Expected class: $_[0]"; bless {} => shift } sub test { print "Actual class: ". ref shift } package main; *Bar:: = \*Foo::; my $x = Bar::new('Bar');

    Now as I understand it, this ought to work because that last line is equivalent to my $x = Bar->new. But it doesn't. In fact it returns Undefined subroutine &Bar::new called at - line 9. So that makes me think that somehow doing a typeglob assignment on the symbol-table hash doesn't do what you'd expect it to. I haven't yet found any relevant docs though.

      Errto

      Your example and error message, makes me think that TYPEGLOB aliasing is basically a very shallow operation. Basically that it does not go any deeper than the top level GLOB. If you were to change the alias line to:

      *Bar::new = \*Foo::new;
      I get the following output:
      Expected class: Bar
      So that makes me think that somehow doing a typeglob assignment on the symbol-table hash doesn't do what you'd expect it to.
      But you aren't doing it on the symbol table hash, you are doing it on the TYPEGLOB. Although oddly enough, if you do this:
      %Bar:: = %Foo::; my $x = Bar::new('Bar');
      I get the same error you got with your example:
      Undefined subroutine &Bar::new
      Now that is something I wouldn't expect to happen.

      -stvn
Re: Two argument bless sometimes ignores the class name?
by dragonchild (Archbishop) on Jan 07, 2005 at 05:24 UTC
    I don't know if this helps, but I think the following statements are equivalent:
    *Bar:: = \*Foo::;
    and
    *main{'Bar::'} = \*main{'Foo::'}

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Two argument bless sometimes ignores the class name?
by !1 (Hermit) on Jan 15, 2005 at 06:42 UTC

    Hmm, this seems to be a trick with bless. At least that's all I can deduce from this snippet:

    #!/usr/bin/perl -l *Bar:: = \*Foo::; print ref bless{}, "Bar"; __END__ Foo

    Haven't seen anything about this in any of the docs but the behavior is consistent since at least 5.6.1.

    Update: Even more curious...

    #!/usr/bin/perl -l sub Foo::j { print "F ",ref shift } sub Bar::j { print "B ",ref shift } $a = bless {}, "Bar"; *Bar:: = \*Foo::; $b = bless {}, "Bar"; print "\$a is type ",ref $a; print "\$b is type ",ref $b; $a->j; Bar::j($a); *{"Bar::j"}{CODE}->($a); $b->j; __END__ $a is type Bar $b is type Foo B Bar B Bar F Bar F Foo

    I think there are some compile-time vs runtime considerations that must be made.

    Update part 2: As far as I understand it, when you bless a reference, the blessed reference will get a pointer to the stash designated by the string you pass as the second parameter. The blessed reference never actually gets the string passed as the second parameter. What ref actually returns is the name on the stash to which the reference points. Thus since Bar points to Foo's stash, you will always get Foo when you bless with Bar.