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

my SUPER:: self even!

In the code below, How can I stop Billy's Mom from introducing herself as "Billy the kid"?

OK, I've got two methods in the parent class.
One of them calls the other.
Both methods are over-ridden in the child.

When the child calls the parent method with $self->Super::firstmethod(), the parent method uses $self->secondmethod().
But the secondmethod() that gets called is the over-ride version, because $self is a reference to to the child object.

How do I get the parent method to call the parent second method?
#!/usr/bin/perl use strict; package person; sub new { my $class = shift(@_); my $self = { 'name' => 'Alice' }; bless($self, $class); return($self); } sub tellName { my($self)=$_[0]; print "$self->{'name'}.\n"; } sub introduce { my($self)=$_[0]; print "Hello, I'm "; $self->tellName(); } 1; package child; our @ISA = qw(person); # inherits from person sub new { my $class = shift(@_); my $self = $class->SUPER::new(); $self ->{'nickName'} = 'Billy The Kid'; bless $self, $class; return $self; } sub tellName { my($self)=$_[0]; print "$self->{'nickName'}.\n"; } sub introduce { my($self)=$_[0]; print "Hi, I'm "; $self->tellName(); print "Here's my mom: "; $self->SUPER::introduce(); } 1; package main; my $mom = new person(); $mom->introduce(); # that time it works. print "\n"; my $son = new child(); $son->introduce();

Replies are listed 'Best First'.
Re: my $self is stumped!
by kyle (Abbot) on Jun 18, 2007 at 02:06 UTC

    You could skip the method dispatch completely and call the sub as a sub. That is, in package person:

    sub introduce { my($self)=$_[0]; print "Hello, I'm "; # $self->tellName(); tellName( $self ); }

    With this change only, the new code produces:

    Hello, I'm Alice. Hi, I'm Billy The Kid. Here's my mom: Hello, I'm Alice.

    I don't recommend that in general, however. For the programmer who comes to your work later (probably you), this could be confusing.

    Update: Another sort of lousy solution:

    package child; sub tellName { my($self)=$_[0]; if ( caller eq __PACKAGE__ ) { print "$self->{'nickName'}.\n"; } else { $self->SUPER::tellName(); } }

    I don't think this is a whole lot better except that it's a lot more obvious that you've done something tricky. I really like ysth's recommendation that the child have a reference to a separate parent object.

      If you truly want to call a method of a fixed class, ignoring polymorphism, then I'd go with a slightly modified version of your first option:
      sub introduce { my($self)=$_[0]; print "Hello, I'm "; # $self->tellName(); # tellName( $self ); person::tellName($self); }
      This makes it very clear what you're doing (always use person's tellName) and isn't as brittle as your second version (if we add a "student" subclass of child, then a student's $self->SUPER::tellName will call child's tellName instead of person's).

      But, as others have said, it's more likely that you don't really want to do this and, if you think you do, you may be confusing classes and instances, so I'd look at it first as a design issue before writing hacky code to make it work.

Re: my $self is stumped!
by ysth (Canon) on Jun 18, 2007 at 02:16 UTC
    It sounds like you are trying to store two different objects in the same object. Instead of having child override anything, give it an additional $self->{mom} attribute that is its own person.

    A really horrible way to fix it would be to have child::introduce do

    my $oldclass = ref $self; bless $self, "person"; print "Here's my mom: "; $self->introduce(); bless $self, $oldclass;
Re: my $self is stumped!
by fenLisesi (Priest) on Jun 18, 2007 at 06:38 UTC
    There seems to be a design problem here. Perhaps you should consider a Human class, each instance of which will have a name and a mom. Or rather, get rid of your class named child and fix person to support both (and rename it Person)
Re: my $self is stumped!
by Anonymous Monk on Jun 18, 2007 at 09:43 UTC
    the basic problem is that, in the op's code, the object created by 'new child' simply has no information about any of the child's antecedents, only about the child. therefore, sending an object reference 'up' to person is pointless.

    this seems like a good place to use a 'clone' object creation approach rather than a 'new' creation from scratch; each new object cloned from an old object can incorporate the information of the old object and add new information.

    package Person; sub create { my $class = shift; my $me = shift; # name of new being # should do some validation here return bless { me => $me } => $class; } use constant ANON => Person->create('unknown'); # some folks have no +antecedents sub bears { my $mom = shift; # mother instance (matrilineal clone) my $me = shift; # kid's name my $dad = shift; # father instance # should do some validation here return bless { me => $me, mom => $mom, dad => $dad } => ref $mom +; } sub ident { my $self = shift; return $self->{me}; } sub mom { my $self = shift; return exists $self->{mom} ? $self->{mom} : ANON; } sub dad { my $self = shift; return exists $self->{dad} ? $self->{dad} : ANON; } my $adam = Person->create('Adam'); # these folks are without antecede +nts my $eve = Person->create('Eve' ); my $able = $eve->bears(Able => $adam); my $mary = $eve->bears(Mary => $adam); my $jane = $eve->bears(Jane => $adam); print "first guy: @{[ $adam->ident ]} \n"; print "first guy's mom: @{[ $adam->mom->ident ]} \n"; print "Jane's mom: @{[ $jane->mom->ident ]} \n"; print "Jane's dad: @{[ $jane->dad->ident ]} \n"; my $fred = $mary->bears(Fred => $able); my $jill = $jane->bears(Jill => $able); print "Fred's dad: @{[ $fred->dad->ident ]} \n"; print "Fred's mom: @{[ $fred->mom->ident ]} \n"; print "Fred's dad's dad: @{[ $fred->dad->dad->ident ]} \n"; print "Fred's dad's mom: @{[ $fred->dad->mom->ident ]} \n"; print "Fred's mom's dad: @{[ $fred->mom->dad->ident ]} \n"; print "Fred's dad's dad's dad: @{[ $fred->dad->dad->dad->ident ]} \n"; print "Fred's dad's dad's dad's dad: @{[ $fred->dad->dad->dad->dad->id +ent ]} \n"; print "Fred's dad's dad's dad's dad's dad: @{[ $fred->dad->dad->dad->d +ad->dad->ident ]} \n"; print "Fred's dad's dad's mom's dad's dad's dad: @{[ $fred->dad->dad-> +mom->dad->dad->dad->ident ]} \n"; my $alan = $jill->bears(Alan => $adam); print "Alan's dad: @{[ $alan->dad->ident ]} \n"; print "Alan's dad's dad: @{[ $alan->dad->dad->ident ]} \n"; print "Alan's mom: @{[ $alan->mom->ident ]} \n"; print "Alan's mom's dad: @{[ $alan->mom->dad->ident ]} \n"; print "Alan's mom's mom: @{[ $alan->mom->mom->ident ]} \n"; print "Alan's mom's mom's mom: @{[ $alan->mom->mom->mom->ident ]} \n";
Re: my $self is stumped!
by atemon (Chaplain) on Jun 19, 2007 at 09:01 UTC

    Hi Monks,

    Here we cant just blame $self. We need to think of few facts.

    We use the code  $self->SUPER::introduce();to call introduce() of package person. Unlike Java or other Object Oriented Languages, 'SUPER' is not an object or reference to the instance of parent. The 'SUPER' just make the parser to look into @ISA for packages containing the method 'introduce()'

    In the very basics of perl Object Orientations itself, we learn that in $obj->method(), $obj is the 1st argument to function 'method()'. In case of  $self->SUPER::introduce();we can see that function is ' SUPER::introduce() ' and NOT' introduce() '. Also, the object passed is  ' $self ' and NOT  ' $self->SUPER '. For 'real' Object Oriented designs, don't we need ' SUPER ' as an object or reference to the instance of base classes(s). (I know there are some limitations)

    Hello Monks, Can you help us to solve this problem, other than creating instance of base classes inside our package?

    --VC

      I'm not sure I agree with your definition of "'real' Object Oriented designs". Many (perhaps even most) OO languages don't treat classes as objects themselves. How, then, can SUPER (or its equivalent) be provided as an object when it isn't one? How can it provide a reference to an instance of a base class which is not instantiated (or which instance should be used if there are several)?

      Perl makes a firm (though not completely hard) division between objects and classes. I've also worked with languages that make no distinction - all classes are objects and all objects are classes - and can't remember ever running into anything that can be done in one style but not the other.

      So what is it that you want to accomplish without creating instances of base classes inside your package? (Probably best to answer in a new post instead of as a comment on this one, though...)

        If I understand it right, in C++, which is where I first learned OOP, instantiating a child object always instantiates a complete object of the base class as well. All of the base class data members and methods are availabe, even if they are overidden by the child.

        The problem that I started with is one that I've never had in C++ (or python as far as I recall). That doesn't mean that perl is "bad" or anything. Just different. I see that there is a different approach to OO in perl than C++. Nothing wrong with different, per se.

        I like all the interesting ideas presented in these solutions. But I didn't use any of them. Most of them require the parent to have some knowledge of the children that may inherit from it. Which is a big OO no-no in my opinion.

        What I ended up doing was replicating some of the parent method's functionality in the child method. This is something that I shouldn't have to do. That's part of the point of inheritance to re-use that code. But I find that with perl, its easier to do that than to go through shenannigans to access parent methods of a parent object that is never actually instantiated...

        Thanks for your many replies. it has been a learning experience, and I will continue to follow the thread and I will return to perl-monks now that I have found such a great community!

Re: my $self is stumped!
by headybrew (Beadle) on Jun 18, 2007 at 16:59 UTC
    Oh great monks...

    Thanks for these responses, they are very interesting. But I can't seem to convince myself that there's no way for a parent class to access it's own methods without being explicitly passed a reference to an object of it's type.

    An additional puzzle: why doesn't this work?
    if ( ref($self) eq 'person') { $self->tellName(); } if ( ref($self) eq 'child') {$self->SUPER::tellName();}
    When I put that into person::introduce, I get this:
    Hello, I'm Alice. Hi, I'm Billy The Kid. Can't locate object method "tellName" via package "person" at ./classt +est line 25. Here's my mom: Hello, I'm

    Which seems to tell me that even when $self was passed in from a child object, it's trying to locate SUPER::tellName inside of package "person".

    Am I interpreting that correctly?

    Complete code with above modification:
    #!/usr/bin/perl use strict; package person; sub new { my $class = shift(@_); my $self = { 'name' => 'Alice' }; bless($self, $class); return($self); } sub tellName { my($self)=$_[0]; print "$self->{'name'}.\n"; } sub introduce { my($self)=$_[0]; print "Hello, I'm "; # $self->tellName(); # original code # tellName($self); # one solution offered by perlmonks #print "\ndebug info: " . ref($self) . "\n"; if ( ref($self) eq 'person'){ $self->tellName(); } if ( ref($self) eq 'child') { $self->SUPER::tellName();} } 1; package child; our @ISA = qw(person); # inherits from person sub new { my $class = shift(@_); my $self = $class->SUPER::new(); $self ->{'nickName'} = 'Billy The Kid'; bless $self, $class; return $self; } sub tellName { my($self)=$_[0]; print "$self->{'nickName'}.\n"; } sub introduce { my($self)=$_[0]; print "Hi, I'm "; $self->tellName(); print "Here's my mom: "; $self->SUPER::introduce(); } 1; package main; my $mom = new person(); $mom->introduce(); # that time it works. print "\n"; my $son = new child(); $son->introduce();

      When one class uses another as its base, and then overrides it, you want that behavior. Sometimes it would be nice for the parent class to be able to take control, but if that where the case then you would have a hard time subclasses other peoples modules to make them do what you want. Realy you just need to look at the problem differently. Since you are making objects you want to think about the actual objects, not the classes. The classes are just a template to build objects with. So your 'person' class isn't 'Alice', it is just a template you are going to use to make the object that will be 'Alice'. In the same fashion your 'child' class isn't 'Billy the kid', it is just a template for an object that has a name, and knows its parents. So when you create the child object you want to tell it it's name and then tell it who its parents are by passing its parents to it as objects. I have modified your code to do exactly that below.

      #!/usr/bin/perl use strict; { package person; #basic person sub new { my ($class, $name) = @_; my $self = { 'name' => $name }; bless $self, $class; return $self; } sub tellName { my ($self) = $_[0]; return $self->{'name'}; } sub introduce { my ($self) = $_[0]; print "Hello, I'm ", $self->tellName(), "\n"; } } { package child; #person + ability to know parents our @ISA = qw(person); # inherits from person sub new { my ($class, $name, $mom, $dad) = @_; my $self = $class->SUPER::new($name); $self->{mom} = $mom; $self->{dad} = $dad; bless $self, $class; return $self; } sub introduce { my($self)=$_[0]; $self->SUPER::introduce(); for (qw/mom dad/) { if ($self->{$_}) { print "Here's my $_: ", $self->{$_}->tellName(); } } } } package main; my $mom = new person("Alice"); $mom->introduce(); # that time it works. print "\n"; my $son = new child("Fred", $mom); $son->introduce();

      The important thing to notice is that the child package is building on the provided definition of a person, adding to that person the ability to remember who its parents are. The packages themselves hold no data, only the objects will hold data. I think partly your use of default names is confusing the issue between class (the description) and the object (what is created using the description as a template). If your person class was able to call its own 'tellName' instead of the inherited one, then you would never have a way to override the behavior of tellName which is exactly the point of inheritance! ;) I hope this helps you.


      ___________
      Eric Hodges

      BTW if you change that to:

      if ( ref($self) eq 'person'){ $self->tellName(); } if ( ref($self) eq 'child') { person::tellName($self);}
      It will then work as expected, though I still wouldn't recommend it because now you have no way to override the behavior of tellName.


      ___________
      Eric Hodges
      An additional puzzle: why doesn't this work?
      if ( ref($self) eq 'person') { $self->tellName(); } if ( ref($self) eq + 'child') {$self->SUPER::tellName();}
      Because perl looks at the code $self->SUPER::tellName(), and says, ah, you want to call the tellName method in the superclass of the current package. It doesn't take $self into account at all. To do that, you need to go beyond what perl itself provides and use something like SUPER or NEXT.