0
votes

I just started to learn Perl. When I moved to object orientation I am getting an error like

Can't locate object method "say_hello" via package "1" (perhaps you forgot to load "1"?) at ./main.pl line 8.

I googled a lot for a solution. Got some similar issues like this. My understanding is it's not a general issue.

Here is my class

# MyModule.pm

package MyModule;
use strict;
use warnings;

sub new {
    print "calling constructor\n";
}

sub say_hello {
    print "Hello from MyModule\n";
}

1;

Here is my test script

# main.pl

#!/usr/bin/perl -w
use strict;
use warnings;

use MyModule;

my $myObj = new MyModule();
$myObj->say_hello();

The code is working perfectly if remove last line from main.pl

2
This ist he perfect time for you to learn about Moose. Check out this talk by RJBS that explains the basics in detail. - simbabque
Side note that neither answer mentions: new MyModule is called indirect object notation and is generally considered bad practice in Perl because it can confuse the parser and cause non-obvious problems. I would recommend getting into the habit of saying MyModule->new instead. - Dave Sherohman
@simbabque: I think it's essential to learn the mechanisms behind Perl's object-oriented support before they are lost behind the screen of Moose and its derivatives. I continue to achieve far better results using Perl to teach OO instead of something like Java or C++, simply because the concepts are so open and clear. - Borodin
@Borodin I agree. But it's still a pain to write it, and Moose is beautiful. Just wanted to mention it, because in the long run it is the right choice for day to day stuff. - simbabque
@simbabque: Yes, it would be ideal for the OP doing "day-to-day stuff". But at present they unclear about ideas like bless, and there is no day-to-day work. Learning about Moose, or preferably its minor variants, should build on top of a thorough understanding of Perl, and I strongly disagree that "This ist he perfect time for you to learn about Moose". - Borodin

2 Answers

8
votes

Your constructor new needs to return a blessed reference to the data structure you are using to contain the object's information. You have no relevant data here, but you still need to return something

bless associates the data with a specific package. In this case, your object should be blessed into MyModule, so that perl knows to look for MyModule::say_hello when it sees a method call like $myObj->say_hello()

Your current constructor returns the value returned by the print statement, which is 1 if it succeeded, as it almost certainly does. That is why you see the "1" in the error message

Can't locate object method "say_hello" via package "1" (perhaps you forgot to load "1"?) at ./main.pl line 8.

The most common container for an object's data is a hash, so you need to change new to this

sub new {

    print "calling constructor\n";

    my $self = { };
    bless $self, 'MyModule';
    return $self;
}

and then your program will work as it should. It creates an anonymous hash and assigns it to the $self variable, then blesses and returns it

Note that this can be made much more concise:

  • Without a return statement, a subroutine will return the value of the most recently executed statement

  • By default, bless will bless the data into the current package

  • There is no need to store the reference in a variable before blessing it

So the same effect may be achieved by writing

sub new {

    print "calling constructor\n";

    bless { };
}

Note also that your call

my $myObj = new MyModule()

is less than ideal. It is called indirect object notation and can be ambiguous. It is better to always use a direct method reference, such as

my $myObj = MyModule->new()

so as to disambiguate the call

5
votes

You're not creating a new object, and thus $myObj is just the return code of the "print" statement (or 1).

You need to bless something and return it.

sub new { 
   my ( $class ) = @_;
   print "Calling Constructor\n";
   my $self = {};
   bless $self, $class;
   return $self;
 }

That way $myObj will actually be an object, not just a return code :)