http://www.perlmonks.org?node_id=1040240

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

Hi Monks,

I want the following code to print out 'son', but it prints out 'parent':

my $obj = Son->new; print $obj->call; package Parent; sub call {char()} sub char {'parent'} package Son; use base 'Parent'; sub new {return bless {}, 'Parent'}; sub char {'son'}

How do you make my dream come true, without putting 'call' subroutine in 'Son' package newly?

Thank you!

Replies are listed 'Best First'.
Re: How to do this?
by kcott (Archbishop) on Jun 22, 2013 at 02:05 UTC

    G'day ryo0ka,

    Your main problem here is that you're ignoring the first argument passed to the class method new() (i.e. Son) and the first argument passed to the object method call() (i.e. $obj).

    A secondary problem is that the constructor, new(), should probably be in the Parent class.

    Here's a rewrite of your code to demonstrate this:

    $ perl -Mstrict -Mwarnings -le ' package Parent; sub new { bless {} => $_[0] } sub call { $_[0]->char } sub char { "parent" } package Son; use base "Parent"; sub char { "son" } package main; my $parent_obj = Parent->new; print "Parent object char = ", $parent_obj->call; my $son_obj = Son->new; print "Son object char = ", $son_obj->call; ' Parent object char = parent Son object char = son

    Additional example:

    While it's absolutely fine that you've posted minimal code to show your problem, it occurred to me that the following might be closer to what you were trying to achieve.

    $ perl -Mstrict -Mwarnings -le ' package Parent; sub new { bless {} => $_[0] } sub call { $_[0]->char } sub char { ref $_[0] } package Son; use base "Parent"; package main; my $parent_obj = Parent->new; print "\$parent_obj class = ", $parent_obj->call; my $son_obj = Son->new; print "\$son_obj class = ", $son_obj->call; ' $parent_obj class = Parent $son_obj class = Son

    Note that there is no longer any hard-coded "parent" or "son" strings and that the Son class now has no methods at all; this code now relies entirely on inheritance to return the same information you had previously with two hard-coded char() methods.

    See how easy it is to extend the class hierarchy without needing to write a custom char() method for every new subclass:

    $ perl -Mstrict -Mwarnings -le ' package Parent; sub new { bless {} => $_[0] } sub call { $_[0]->char } sub char { ref $_[0] } package Son; use base "Parent"; package Daughter; use base "Parent"; package Grandson; use base "Son"; package main; my $parent_obj = Parent->new; print "\$parent_obj class = ", $parent_obj->call; my $son_obj = Son->new; print "\$son_obj class = ", $son_obj->call; my $daughter_obj = Daughter->new; print "\$daughter_obj class = ", $daughter_obj->call; my $grandson_obj = Grandson->new; print "\$grandson_obj class = ", $grandson_obj->call; ' $parent_obj class = Parent $son_obj class = Son $daughter_obj class = Daughter $grandson_obj class = Grandson

    -- Ken

      Thank you Ken so much!! I really appreciate your examples :DD
Re: How to do this?
by rjt (Curate) on Jun 22, 2013 at 23:17 UTC

    To unlock considerably more power in object-oriented applications, as well as dissolving some of the lumps in Perl's syntactic sugar, you may want to check out the very popular Moose (available on CPAN).

    With Moose, your example can be written succinctly as follows:

    #!/usr/bin/env perl use 5.010; package Father; use Moose; sub char { lc ref shift } package Son; use Moose; extends 'Father'; package main; say ref($_) . '->char() = ' . $_->char for new Father, new Son;

    Output:

    Father->char() = father Son->char() = son

    You'll notice that I didn't have to explicitly create any constructors, nor did I have to monkey with any of the unintuitive bless machinations normally required to instantiate objects and work with subclasses.

    I would probably take this a step further with a couple more features of Moose to turn the char() method into a proper class attribute:

    has 'char' => ( is => 'ro', builder => '_build_char', # May override _build_char in subclasse +s init_arg => undef, # Disallow setting via constructor ); sub _build_char { lc ref shift }

    On the surface, this doesn't change much: the output is the same, and thanks to init arg => undef and is => 'ro', its value is immutable just like the original char() method. However, rather than fetch and convert its reference type to lowercase every time it's called, the builder sub only fetches the result once, when the object is instantiated. For this example, the savings are small, but for more computationally expensive things, it will be significant. Perhaps more importantly, do not underestimate the expressive power of the has syntax; this makes it very clear what char does and what its limitations are.

    I'm by no means a Moose evangelist (Moosevangelist?), but for someone learning Perl OO for the first time, I see no particularly compelling reason to learn the hard way. Over time, sure, you will pick up the knowledge of what's going on under the hood, but most of the time, you just don't need to bother.

    If you're willing to put up with a many more module dependencies (around 50!), MooseX::Declare gives you even more syntactic sugar. The class definitions become:

    use MooseX::Declare; class Father { has char => ( is => 'ro', builder => '_build_char', init_arg => undef, ); sub _build_char { lc ref shift } } class Son extends Father { }

    How far you go should depend on the complexity of your object taxonomy.

Re: How to do this?
by sundialsvc4 (Abbot) on Jun 22, 2013 at 22:39 UTC

    Ex minimis, in your code you are saying that Son is aParent ... hence, the object that it (incorrectly ...) produces will never search for its methods in Son, even though the constructor method is defined there.   The search for classes will instead look in Parent, as you see it doing.   Constructors in Perl are, as previously noted, told what class-name to use.   A little crazy, but you do get used to it.