Problem about type glob reference

T

Todd

I meet with a inconsistent behavior about the type glob reference
blow. Here Case3 works fine. But Case1 and Case2 file a compile error.
The difference between Case2 and Case3 is that I move the typeglob
assignment from inside SCOPE to outside SCOPE. I'm trying to
understand this, may you help me?

Case1:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&foo;
}

&main::foo();

__END__
Undefined subroutine &main::foo called at - line 9.


Case2:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
}

&main::foo();

__END__
Undefined subroutine &main::foo called at - line 9.

Case3:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
}

$main::{foo} = \&Foo::foo;
&main::foo();

__END__

foo called

Thanks,
Todd
 
T

Todd

Todd said:
$main::{foo} = \&foo;
I know the better one is
*main::foo = \&foo;
Or even better: use Exporter and use.

But this doesn't explain my question. Hopefully some useful hints.

Thanks,
Todd
 
P

Peter Scott

#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&foo;
}

&main::foo();

__END__
Undefined subroutine &main::foo called at - line 9.

I only have a few minutes to spare here before my battery runs out, but
what's weird about this is (a) it works if you do something with foo()
inside the BEGIN block, and (b) the result is different on 5.10.

End the BEGIN block with, say,

foo();
or
my $x = *main::foo{CODE};

and it works. Try it on 5.10 and the error when calling &main::foo
changes to "Cannot convert a reference to CODE to typeglob".
 
C

comp.llang.perl.moderated

I only have a few minutes to spare here before my battery runs out, but
what's weird about this is (a) it works if you do something with foo()
inside the BEGIN block, and (b) the result is different on 5.10.

End the BEGIN block with, say,

foo();

In this case, Foo::foo() is called however:

BEGIN { ...
sub foo { print "foo called by ",(caller)[0];}
foo();
}

foo called by Foo

A package declaration has lexical scope and
'main' is still undefined within BEGIN. This
though would also work:


BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&foo;
package main;
foo()
}
or
my $x = *main::foo{CODE};

and it works. Try it on 5.10 and the error when calling &main::foo
changes to "Cannot convert a reference to CODE to typeglob".

No idea why the later works though..
 
T

Todd

Peter said:
End the BEGIN block with, say,

foo();
or
my $x = *main::foo{CODE};

and it works. Try it on 5.10 and the error when calling &main::foo

So these 2 works below:

#! /bin/perl -l
BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
my $x = *main::foo{CODE};
}

&main::foo();

__END__
foo called


#! /bin/perl -l
{
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
}

&main::foo();

__END__

foo called

Seems it's related to the subtle meaning of BEGIN and reference count,
can any one give a explanation about this?

-Todd
 
C

comp.llang.perl.moderated

So these 2 works below:

#! /bin/perl -l
BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
my $x = *main::foo{CODE};
}

&main::foo();

__END__
foo called

#! /bin/perl -l
{
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
}

&main::foo();

__END__

foo called

Seems it's related to the subtle meaning of BEGIN and reference count,
can any one give a explanation about this?

You might be right but somehow I think there's
more to it.. including lexical package scope.
Just a simple declaration of 'main' inside
BEGIN {} works:

#! /bin/perl -l

BEGIN {
package main;
sub foo { print "foo called..."; }
}

foo();
__END__
foo called...

Note 'foo' also becomes available via 'use subs' occurring at compile
time too:

use subs 'foo';
BEGIN {
package Foo;
sub foo { print "foo called...'; }
$main::{foo} = \&Foo::foo;
# my $x = *main::foo{CODE};
}
foo();
__END__
foo called...
 
C

comp.llang.perl.moderated

I meet with a inconsistent behavior about the type glob reference
blow. Here Case3 works fine. But Case1 and Case2 file a compile error.
The difference between Case2 and Case3 is that I move the typeglob
assignment from inside SCOPE to outside SCOPE. I'm trying to
understand this, may you help me?

Case1:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&foo;
}

&main::foo();

__END__
Undefined subroutine &main::foo called at - line 9.

Case2:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
$main::{foo} = \&Foo::foo;
}

&main::foo();

__END__
Undefined subroutine &main::foo called at - line 9.

Case3:
#! /bin/perl -l

BEGIN {
package Foo;
sub foo { print "foo called"; }
}

$main::{foo} = \&Foo::foo;
&main::foo();

__END__

foo called

Reducing all these to a simple example:

BEGIN {
sub Foo::foo { print "foo called..."; }
$main::{foo} = \&Foo::foo; # assign ref.
}

foo(); # fails with "undef. subroutine ...


However, as noted earlier, moving the 'assign ref.' line above outside
BEGIN {} just before the foo() call works. Adding 'use vars "foo"' at
the top works too. Suprisingly so does adding '$::foo = 1' inside --
but not outside -- BEGIN {} does too.

With these diverse solutions, I suspect Perl has some heuristic about
whether to discard the glob assignment but I really doubt it's just a
ref. count.
 
P

Peter Scott

Reducing all these to a simple example:

BEGIN {
sub Foo::foo { print "foo called..."; }
$main::{foo} = \&Foo::foo; # assign ref.
}

foo(); # fails with "undef. subroutine ...


However, as noted earlier, moving the 'assign ref.' line above outside
BEGIN {} just before the foo() call works. Adding 'use vars "foo"' at
the top works too. Suprisingly so does adding '$::foo = 1' inside --
but not outside -- BEGIN {} does too.

With these diverse solutions, I suspect Perl has some heuristic about
whether to discard the glob assignment but I really doubt it's just a
ref. count.

It is not exactly discarding the glob assignment. Try it on 5.10 and see
what error you get.

As a p5p poster pointed out, the assignment should be:

*main::foo = \&foo;

and all is right with the world. That doesn't explain what is going on
with this code, but it does solve the OP's problem.
 
C

comp.llang.perl.moderated

It is not exactly discarding the glob assignment. Try it on 5.10 and see
what error you get.

As a p5p poster pointed out, the assignment should be:

*main::foo = \&foo;


Interesting, that does the trick as well. I'm
confused why the alternate *main{foo} doesn't
work as well though. *main::foo is accessed
more efficiently at compile time but $main::{foo}
won't create a new typeglob if none previously
exists. So, I'm not sure why the former should
viewed preferentially...
and all is right with the world. That doesn't explain what is going on
with this code, but it does solve the OP's problem.

It also doesn't explain why 'use vars "foo"'
or '$::foo = 1' in the BEGIN causes all to be
"right with the world" :)

Thanks for the info.
 
B

Ben Morrow

Quoth "comp.llang.perl.moderated said:
Interesting, that does the trick as well.

That's unsurprising: it's the supported and documented way of doing
this.
I'm confused why the alternate *main{foo} doesn't work as well
though.

Huh? I think you need to read perlref again. Typeglobs don't have a foo
slot, and even if they did it would have nothing to do with any sub
named &foo in any package.
*main::foo is accessed more efficiently at compile time but
$main::{foo} won't create a new typeglob if none previously exists.

....which is the root of the problem. %main:: is 'just' an ordinary hash:
it doesn't know it's a symbol table, and it doesn't know it should only
contain globs. So

$main::{foo} = \&Foo::foo;

puts a subref directly in the symbol table, where a glob should be.
Unsurprisingly, this doesn't work: when perl later tries to look up
&main::foo, it can't find a typeglob for it. 5.10 gives a slightly more
helpful message than 5.8, probably because 5.10 *does* sometimes expect
to find things other than typeglobs in the symbol table. For instance:

~% perl5.8.8 -le'BEGIN{ $main::{foo} = \2 } print foo'
Undefined subroutine &main::foo called at -e line 1.
~% perl5.10.0 -le'BEGIN{ $main::{foo} = \2 } print foo'
2
~%
It also doesn't explain why 'use vars "foo"'
or '$::foo = 1' in the BEGIN causes all to be
"right with the world" :)

*Any* reference to *main::foo before perl reaches the assignment causes
$main::{foo} to be filled with a typeglob automatically. So even
removing the BEGIN fixes the problem, as when perl compiles the call to
&main::foo it creates the typeglob, so by the time it gets to the
assignment (at runtime) there is already a glob there. At that point
normal glob assignment magic kicks in.

Ben
 
C

comp.llang.perl.moderated

<snip: $main::{foo} outside BEGIN works>





That's unsurprising: it's the supported and documented way of doing
this.


Huh? I think you need to read perlref again. Typeglobs don't have a foo
slot, and even if they did it would have nothing to do with any sub
named &foo in any package.

Typo... I meant $main{foo} of course...
...which is the root of the problem. %main:: is 'just' an ordinary hash:
it doesn't know it's a symbol table, and it doesn't know it should only
contain globs. So

$main::{foo} = \&Foo::foo;

puts a subref directly in the symbol table, where a glob should be.
Unsurprisingly, this doesn't work: when perl later tries to look up
&main::foo, it can't find a typeglob for it. 5.10 gives a slightly more
helpful message than 5.8, probably because 5.10 *does* sometimes expect
to find things other than typeglobs in the symbol table. For instance:

~% perl5.8.8 -le'BEGIN{ $main::{foo} = \2 } print foo'
Undefined subroutine &main::foo called at -e line 1.
~% perl5.10.0 -le'BEGIN{ $main::{foo} = \2 } print foo'
2
~%



*Any* reference to *main::foo before perl reaches the assignment causes
$main::{foo} to be filled with a typeglob automatically. So even
removing the BEGIN fixes the problem, as when perl compiles the call to
&main::foo it creates the typeglob, so by the time it gets to the
assignment (at runtime) there is already a glob there. At that point
normal glob assignment magic kicks in.

Great explanation. I'll re-read perlref.
 
B

Ben Morrow

Quoth "comp.llang.perl.moderated said:
Typo... I meant $main{foo} of course...

No, you didn't... :)

$main{foo} is an element of %main, aka %main::main, a perfectly ordinary
hash. $main::{foo} is an element of %main::, which is the symbol table
for the 'main' package, and (should) contain raw globs rather than
ordinary scalars.
Great explanation. I'll re-read perlref.

What documentation there is of this is in perlmod, under 'Symbol
Tables', but it's not terribly clear. The only correct reference for
things like this is the source, of course :).

Ben
 

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,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top