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 ...

+ Reply to Thread
Page 1 of 2 1 2 LastLast
Results 1 to 10 of 11

What is his son's father's name?

  1. Default 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

  2. Default 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?

  3. Default 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

  4. Default 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

  5. Default 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.

  6. Default 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

  7. Default 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

  8. Default 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 }


  9. Default 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/


  10. Default 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


+ Reply to Thread
Page 1 of 2 1 2 LastLast

Similar Threads

  1. Re: What is his son's father's name?
    By Application Development in forum Perl
    Replies: 0
    Last Post: 01-27-2005, 02:01 PM