What is his son's father's name? - Perl
This is a discussion on What is his son's father's name? - Perl ; I am having problems with allowing a subclass to extend some parts of a
method while inheriting the rest of the algorithm.
I managed to squeeze the problem down to a very short demo, which
exhibits the problem without the ...
-
What is his son's father's name?
I am having problems with allowing a subclass to extend some parts of a
method while inheriting the rest of the algorithm.
I managed to squeeze the problem down to a very short demo, which
exhibits the problem without the more complex machinery of partial
algorithm inheritance.
Take a parent class:
package Parent;
sub xyz { print "@_\n" }
sub abc { shift->SUPER::xyz }
sub new { bless {}, __PACKAGE__ }
1;
and a subclass:
package Child;
use base qw(Parent);
1;
and run:
perl -e 'use Child; my $x = Child->new; $x->abc'
With AS Perl 5.6.1, this produces a surprising error:
Can't locate object method "xyz" via package "Parent" (perhaps you
forgot to load "Parent"?) at Parent.pm line 3.
What am I doing wrong?
Heini
-
Re: What is his son's father's name?
On Thu, Jan 27, 2005 at 06:03:37PM +0000, Heini wrote:
> I am having problems with allowing a subclass to extend some parts of a
> method while inheriting the rest of the algorithm.
>
> I managed to squeeze the problem down to a very short demo, which
> exhibits the problem without the more complex machinery of partial
> algorithm inheritance.
>
> Take a parent class:
>
> package Parent;
> sub xyz { print "@_\n" }
> sub abc { shift->SUPER::xyz }
> sub new { bless {}, __PACKAGE__ }
> 1;
>
> and a subclass:
>
> package Child;
> use base qw(Parent);
> 1;
>
> and run:
>
> perl -e 'use Child; my $x = Child->new; $x->abc'
>
> With AS Perl 5.6.1, this produces a surprising error:
>
> Can't locate object method "xyz" via package "Parent" (perhaps you
> forgot to load "Parent"?) at Parent.pm line 3.
SUPER is not relative to the object being worked with; it is relative to
the class it occurs in. And Parent has no superclass.
I'm not sure what effect you are trying to have by specifying SUPER.
Does shift->xyz not do what you want?
-
Re: What is his son's father's name?
Yitzchak Scott-Thoennes wrote:
>
> On Thu, Jan 27, 2005 at 06:03:37PM +0000, Heini wrote:
> >
> > With AS Perl 5.6.1, this produces a surprising error:
> >
> > Can't locate object method "xyz" via package "Parent" (perhaps you
> > forgot to load "Parent"?) at Parent.pm line 3.
>
> SUPER is not relative to the object being worked with; it is relative to
> the class it occurs in. And Parent has no superclass.
But if that was true, the wording in the error message would be
different, wouldn't it? It is the eror message that doesn't make any
sense to me, none at all.
Is the behavior identical in all later perl versions?
Heini
-
Re: What is his son's father's name?
Yitzchak Scott-Thoennes wrote:
>
> I'm not sure what effect you are trying to have by specifying SUPER.
> Does shift->xyz not do what you want?
Okay, here's an example which, despite its brevity, hopefully makes some
sense. The child class remains the same, but the parent's methods
change:
package Parent;
%Parent::class_attr = ( xyz => "Hello world!" );
sub xyz {
my $self = shift;
my $value = $self->{xyz};
return $value if defined $value;
if ( my $class = ref $self ) {
$class .= q(::class_attr);
no strict q(refs);
$value = $class->{xyz};
return $value if defined $value;
}
$self->SUPER::xyz; # Won't be reached anymore.
}
sub abc { print ( shift->xyz . "\n" ) }
sub new { bless {}, shift }
1;
With this setup, the result is:
$ perl -e 'use Parent; my $x = Parent->new; $x->abc'
Hello world!
$ perl -e 'use Child; my $x = Child->new; $x->abc'
Can't locate object method "xyz" via package "Parent" (perhaps you
forgot to load "Parent"?) at Parent.pm line 17.
So you are saying that SUPER is statically scoped, and I was expecting a
dynamic scope. What follows it that either I have to implement the
dynamic SUPER behavior myself, or forget all about it. If we stick to
the attribute inheritance example, I can make it work without SUPER
simply by adding one line to teh child class:
package Child;
use base qw(Parent);
%Child::class_attr = %Parent::class_attr;
1;
Anyhow, this would mean I have to ask reusers to add that one line to
all subclasses, and I do not want to pose such demands (although, I have
to admit, it gives much better performance). And in any case, the
attribute example was just an example, not the general case of
inheriting algorithms partially.
Heini
-
Re: What is his son's father's name?
Heini wrote:
> Yitzchak Scott-Thoennes wrote:
>>
>> I'm not sure what effect you are trying to have by specifying SUPER.
>> Does shift->xyz not do what you want?
>
> Okay, here's an example which, despite its brevity, hopefully makes some
> sense. The child class remains the same, but the parent's methods
> change:
What you really want is a way to go up the hierarchy looking for
%class_attr. Which perl does not really do - classes don't inherit
class variables, only methods.
> So you are saying that SUPER is statically scoped, and I was expecting a
> dynamic scope. What follows it that either I have to implement the
It is scoped from the current __PACKAGE__. In this case, xyz is in
Parent's package, so SUPER will look from there, and will fail to find
any parent.
Note that even if it worked, your $self object would still be a Child
ref, so you'd end up in an infinite loop.
The way to do this in perl - well, the straight-forward way anyway, is
to change xyz to:
sub xyz {
my $self = shift;
my $value = $self->{xyz};
return $value if defined $value;
return $class_attr{xyz};
}
Then, in Child, you have a new xyz... which, really, is pretty much
identical:
sub xyz {
my $self = shift;
my $value = $self->{xyz};
return $value if defined $value;
return $class_attr{xyz} || $self->SUPER::xyz();
}
This assumes that Child has its own %class_attr - if not, then just
skip the function altogether.
The less straight-forward way is to provide the dynamic lookup
yourself. I've done this for subroutines (I wanted all xyz's to be
called in order, without requiring the user to call SUPER) in the past,
but it is somewhat messy.
-
Re: What is his son's father's name?
Darin McBride wrote:
>
> Note that even if it worked, your $self object would still be a Child
> ref, so you'd end up in an infinite loop.
Aw, that might be true.
> The way to do this in perl - well, the straight-forward way anyway, is
> to change xyz to:
>
> sub xyz {
> my $self = shift;
> my $value = $self->{xyz};
> return $value if defined $value;
>
> return $class_attr{xyz};
> }
>
> Then, in Child, you have a new xyz... which, really, is pretty much
> identical:
That is exactly waht I am trying to avoid - having to ask each reuser to
rewrite the method, in stead of simply inheriting it.
To finish my own (artificial) example, one way to implement the dynamic
SUPER behavior is to replace the line
$self->SUPER::xyz;
by this:
# Cannot use SUPER:: here because its scope is static.
my $isa = ( ref $self || $self ) . q(::ISA);
for my $super ( @$isa ) {
return $super->xyz if $super->can( 'xyz' );
}
return undef;
but it's probably better to stick to the same inheritance tree and check
for the parent class
return $super->xyz if $super->isa( __PACKAGE__ );
instead of the "$super->can('xyz')" condition.
Heini
-
Re: What is his son's father's name?
Darin McBride wrote:
>
> It is scoped from the current __PACKAGE__. In this case, xyz is in
> Parent's package, so SUPER will look from there, and will fail to find
> any parent.
That was true in my original example, which was way too short, but not
in te more elaborated one.
> Note that even if it worked, your $self object would still be a Child
> ref, so you'd end up in an infinite loop.
No, there's no loop. You forget that there are two methods involved
here: the generic algorithm "abc", which I do not expect subclasses to
override (but they might), and the specific fragment "xyz", which I do
expect the subclasses to override (but they might not). When "abc" is
called on a child object, we back up the inheritance tree and find it
from the parent class. When "abc" calls "xyz", I want the backing up of
the inheritance tree to again start from the child, and only end up back
to parent if "xyz" was not overridden after all. There is no loop
involved here.
For completeness, I give here the whole example in a verified, runnable
form. The child is the same as before:
package Child;
use base qw(Parent);
1;
and the parent after all the modifications:
package Parent;
%Parent::class_attr = ( xyz => "Hello world!" );
sub new { bless {}, shift }
sub abc { print ( shift->xyz . "\n" ) }
sub xyz {
my $self = shift;
no strict q(refs);
my $value;
my $class = ref $self;
if ( $class ) {
$value = $self->{xyz};
return $value if defined $value;
}
$class ||= $self;
my $class_attr = $class . q(::class_attr);
$value = $class_attr->{xyz};
return $value if defined $value;
# Cannot return $self->SUPER::xyz here
# because the scope of SUPER:: is static.
my $isa = $class . q(::ISA);
for my $super ( @$isa ) {
return $super->xyz if $super->can( 'xyz' );
}
die qq(Cannot locate method "xyz" via package "$class");
}
1;
Now the both give the same result:
$ perl -e 'use Parent; my $x = Parent->new; $x->abc'
Hello world!
$ perl -e 'use Child; my $x = Child->new; $x->abc'
Hello world!
It is ironic that the example I planned to be a simple one grew much
more complicated than the changes I had to make into the real-world code
I originally had at hands.
Heini
-
Re: What is his son's father's name?
Heini wrote:
> Darin McBride wrote:
>
>>The way to do this in perl - well, the straight-forward way anyway, is
>>to change xyz to:
>>
>>sub xyz {
>> my $self = shift;
>> my $value = $self->{xyz};
>> return $value if defined $value;
>>
>> return $class_attr{xyz};
>>}
>>
>>Then, in Child, you have a new xyz... which, really, is pretty much
>>identical:
>
>
> That is exactly waht I am trying to avoid - having to ask each reuser to
> rewrite the method, in stead of simply inheriting it.
You appear to be focusing on what you are trying to avoid and have lost
track of what you are trying to achieve.
If you want to refer to a package variable always in a specific package
then simply use a fully qualified name.
If you want something that can be inherited and overridden make it a method.
If you want a method defined in a child class to call methods in it's
parent class and ingore overrides in itself or its decendants then use
the special prefix SUPER::. When you do not want the semantics of SUPER
then do not use it. (Aside: when you put a namespace prefix on the
method name then the class of the obejct is in fact completely ignored.
This has been known to introduce security holes when people use
symbolc method refs in the mistaken belief that the fact that they are
exempt from strict refs means they are in some way more secure. )
If you think the error...
Can't locate object method "xyz" via package "Parent" (perhaps
you forgot to load "Parent"?)
.... is missleading when you try to call Parent->SUPER::xyz then you are
abosolutely correct, it would be clearer if it said...
Can't locate object method "SUPER::xyz" via package "Parent"
....but you shouldn't let the imperfection of the error message distract
you from what is happening.
All the complexities you appear to be encountering seem to be because
you've chosen the wrong tool in the first place.
Take a step back and start again. To take essentially your OP:
Take a parent class:
package Parent;
sub xyz { print "@_\n" }
sub abc { my $self = shift; $self->xyz(@_) }
sub new { bless {}, shift }
1;
and a subclass:
package Child;
use base qw(Parent);
1;
perl -e 'use Child; my $x = Child->new; $x->abc("foo")'
This will print "foo\n".
OK, now that's working, what else did you want to do?
You have a package variable %Parent::class_attr that you want to refer
to in package Child? No problem just do so. No playing arround, just
do it: %Parent::class_attr.
If you don't like this you can create an accessor in Parent that will be
inherited by Child:
package Parent;
sub class_attr { \%class_attr }
-
Re: What is his son's father's name?
In article <41FB7446.D38E8046@luukku.com>,
Heini <heini.cciug@luukku.com> writes:
>For completeness, I give here the whole example in a verified, runnable
>form. The child is the same as before:
>
> package Child;
> use base qw(Parent);
> 1;
>
>and the parent after all the modifications:
>
> package Parent;
> %Parent::class_attr = ( xyz => "Hello world!" );
> sub new { bless {}, shift }
> sub abc { print ( shift->xyz . "\n" ) }
> sub xyz {
> my $self = shift;
> no strict q(refs);
> my $value;
>
> my $class = ref $self;
> if ( $class ) {
> $value = $self->{xyz};
> return $value if defined $value;
> }
>
> $class ||= $self;
> my $class_attr = $class . q(::class_attr);
> $value = $class_attr->{xyz};
> return $value if defined $value;
>
> # Cannot return $self->SUPER::xyz here
> # because the scope of SUPER:: is static.
> my $isa = $class . q(::ISA);
> for my $super ( @$isa ) {
> return $super->xyz if $super->can( 'xyz' );
> }
> die qq(Cannot locate method "xyz" via package "$class");
> }
> 1;
>
>Now the both give the same result:
>
>$ perl -e 'use Parent; my $x = Parent->new; $x->abc'
>Hello world!
>$ perl -e 'use Child; my $x = Child->new; $x->abc'
>Hello world!
If I understand you correctly, you want to implement inheritable
class attributes (unless your example above is simply
contrived for another purpose), by just defining them with a
hash in each package. In which case, you'd end up
writing the same code for every other attribute, no? How
about parameterizing it with AUTOLOAD. And at the same time,
use Class::ISA to get the inheritance tree properly:
% cat /tmp/foo
#!/usr/local/bin/perl
use strict;
use warnings;
my $f = Foo->new;
print "xyz: ", $f->xyz, "\n";
print "abc: ", $f->abc, "\n";
print "def: ", $f->def, "\n";
BEGIN {
use Class::ISA;
no warnings 'once';
# Child class Foo
@Foo::ISA = 'Bar';
%Foo::class_attr = ( xyz => 'Overridden' );
# Parent class Bar
%Bar::class_attr = (abc => 'Parent abc',
xyz => 'Parent xyz' );
sub Bar::new { bless {}, shift }
sub Bar::AUTOLOAD {
no strict 'refs';
my ($class, $method) = ($Bar::AUTOLOAD =~ /(.*):
.*)/);
return if $method eq 'DESTROY';
for my $super ($class, Class::ISA::super_path($class))
{
if (exists ${"${super}::class_attr"}{$method})
{
return ${"${super}::class_attr"}{$method};
}
}
die "No value for $method\n";
}
}
% /tmp/foo
xyz: Overridden
abc: Parent abc
No value for def
--
Peter Scott
http://www.perldebugged.com/
*** NEW *** http://www.perlmedic.com/
-
Re: What is his son's father's name?
Peter Scott wrote:
>
> If I understand you correctly, you want to implement inheritable
> class attributes (unless your example above is simply
> contrived for another purpose), by just defining them with a
> hash in each package. In which case, you'd end up
> writing the same code for every other attribute, no?
I am really sorry for the unfortunate "simplified" example I tried to
provide. In my real code, it was not xyz calling itself, but abc calling
xyz (as in my first example, which was otherwise too short), and the
only change I needed really was to replace
return $self->SUPER::xyz;
with
my $class = ref $self|| $self;
my $isa = $class . q(::ISA);
for my $super ( @$isa ) {
return $super->xyz if $super->isa( __PACKAGE__ );
}
croak qq(Cannot locate object method "xyz" via package "$class");
- except for one important detail: instead of "xyz" in the above, I have
a variable (and in fact, the $class variable does get set already
earlier, but that's not important here).
> How about parameterizing it with AUTOLOAD.
I am already using it for another purpose in the same class.
> And at the same time,
> use Class::ISA to get the inheritance tree properly:
This is new to me. Probably not available in Perl 5.6.1, which I have to
stick to?
Heini
Similar Threads
-
By Application Development in forum Perl
Replies: 0
Last Post: 01-27-2005, 02:01 PM