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

I'm trying to learn how inheritance works in Perl 5. I have big problems to make it work and I can not understand what I´m doing wrong.
#!/user/bin/perl -w use strict; my $horse1 = Horse->new("George the Horse", 12); $horse1->present(); $horse1->animalmethod(); ############################ ############################ package Animal; { sub animalmethod { my ($self)=@_; print "I´m an animal. My name is $self->{Name}. I´m $self->{Age} y +ears old\n"; } } package Horse; { our @ISA=("Animal"); sub new { my ($class, $name, $age) = @_; my $ref = {Name=>$name, Age=>$age}; bless($ref, $class); } sub present { my ($self)=@_; if($self->isa("Animal")) { print "I'm an animal.\n"; } else { print "I'm not an animal.\n"; } } }
The code above contains a animal class and a horse class. The ISA-array should as I understand it instruct the hosre class to inherit from the animalclass. Thus the horse should be an animal and be able to use animal methods. When I run the code however the result is:

I´m not an animal. Can´t locate object method animalmethod via package "Horse".

I´m writing excactly as it is written in the OO-manuals but it still doesn´t work. Could anyone be kind and tell me what the problem is?

Replies are listed 'Best First'.
Re: Inheritance in Perl5
by chromatic (Archbishop) on Aug 08, 2009 at 18:12 UTC

    The other comments are wrong; the problem is that the assignment to @Horse::ISA occurs after the creation of the $horse1 object and subsequent method calls. Put a BEGIN block around our @ISA=("Animal"); and your code will work.

    If Perl 5 had a declarative class syntax where subclassing relationships happened at compilation time, this would not be a problem.

Re: Inheritance in Perl5
by FunkyMonk (Bishop) on Aug 08, 2009 at 13:40 UTC
    Perl tends to like its packages defined before they're used. Try putting both your packages inside a BEGIN block. ie
    my $horse1 = Horse->new("George the Horse", 12); $horse1->present(); $horse1->animalmethod(); BEGIN { package Animal; ... package Horse; ... }

    Update:

    I forgot I was going to point you towards Moose. It's a nicer way of writing OO Perl. Then, try MooseX::Declare for a much nicer way.

      Perl tends to like its packages defined before they're used.

      Indeed, this is why the Perl 5 parser creates namespaces and installs subroutines into those name spaces at compilation time.

      Except for code which creates an object in a BEGIN block before the parser has even read the class package declaration, this order does not matter. The OP's question is similar to:

      ok( foo(), 10, 'foo() should return value of $bar' ); my $bar = 10; sub foo { return $bar }
      The moose way you speak of:
      package Animal; use Moose; has [qw/name age/] => ( isa => 'Str', is => 'rw' ); package Horse; use Moose; extends 'Animal'; sub present { my $self = shift; print $self->isa('Animal') ? "I am an animal" : "I am not animal" }
      With that said, I think there is something seriously wrong if you want your subclass to confirm that it inherits from the parent class in a method ;)


      Evan Carroll
      The most respected person in the whole perl community.
      www.EvanCarroll.com
Re: Inheritance in Perl5
by Bloodnok (Vicar) on Aug 08, 2009 at 13:45 UTC
    The declaration order is the problem - perl doesn't know about the locally declared classes in advance - try re-writing to read:
    #!/user/bin/perl -w use strict; ############################ ############################ package Animal; { sub animalmethod { my ($self)=@_; print "I´m an animal. My name is $self->{Name}. I´m $self->{Age} y +ears old\n"; } } package Horse; { our @ISA=("Animal"); sub new { my ($class, $name, $age) = @_; my $ref = {Name=>$name, Age=>$age}; bless($ref, $class); } sub present { my ($self)=@_; if($self->isa("Animal")) { print "I'm an animal.\n"; } else { print "I'm not an animal.\n"; } } } package main; my $horse1 = Horse->new("George the Horse", 12); $horse1->present(); $horse1->animalmethod();
    Doing so, then running the script gives
    $ perl tst.pl I'm an animal. I´m an animal. My name is George the Horse. I´m 12 years old
    as expected.

    A user level that continues to overstate my experience :-))
Re: Inheritance in Perl5
by ikegami (Patriarch) on Aug 08, 2009 at 15:30 UTC
    It makes more sense to place the package directive *in* the curlies since the package directive is lexically scoped.