Perl, Moose - subclass is not inheriting methods of a superclass - perl

I'm new to Perl and have been directed to Moose as the go to source for Perl OO but I am having some problems with making it work. Specifically, methods of the superclass do not seem to be inherited.
To test this I have created three files which contain the following:
thingtest.pl
use strict;
use warnings;
require "thing_inherited.pm";
thing_inherited->hello();
thing.pm
package thing;
use strict;
use warnings;
use Moose;
sub hello
{
print "poo";
}
sub bye
{
print "naaaa";
}
1;
And finally, thing_inherited.pm
package thing_inherited;
use strict;
use warnings;
use Moose;
extends "thing";
sub hello
{
bye();
}
1;
So what one would normally expect is for method bye to be inherited as part of the subclass but I am given this error instead...
Undefined subroutine &thing_inherited::bye called at thing_inherited.pm line 11.
Can anyone explain if I'm doing something wrong here? Thanks!
edit: In doing this I have come across another conundrum: calling a method in my base class from my superclass that has should have been overwritten by the superclass is not overwritten.
Say I had
sub whatever
{
print "ignored";
}
in my base class and added
whatever();
in my bye method, calling bye would not produce the overwritten result, only print "ignored".

You have a function call, not a method call. Inheritance only applies to classes and objects, i.e. method calls. A method call looks like $object->method or $class->method.
sub hello
{
my ($self) = #_;
$self->bye();
}
By the way, require "thing_inherited.pm"; should be use thing_inherited;

Just a few fixes. thing.pm was fine, but see the changes to thingtest.pl and thing_inherited.pm.
thingtest.pl: need to instantiate the object before using it, then you can use the method
use thing_inherited;
use strict;
use warnings;
my $thing = thing_inherited->new();
$thing->hello();
thing_inherited.pm: since the hello method is calling a method in it's class, you need to tell it just that
package thing_inherited;
use strict;
use warnings;
use Moose;
extends "thing";
sub hello {
my $self = shift;
$self->bye();
}
1;
Output:
$ perl thingtest.pl
naaaa$

Related

Perl : Like in java we can access public member (variables) in other class is there any same concept in perl

I have 2 perl file and i want to use value of one variable in another perl file as input so how i can do it is there any concept like java we can declare it as public and use it.
any help appreciated thank you!
In this answer, I'll skip the discussion about whether it is the right decision to use OOP or not and just assume you want to do it the OOP-way.
In short, all variables of an object in Perl can be considered public. In fact, the problem is often the opposite - to make some of them private. Anyway, if you have a file Obj.pm which defines an object with a field foo which looks like this:
package Obj;
sub new {
my $class = shift;
my $self = {foo => "bar"};
bless $self, $class;
return $self;
}
you can access the foo variable as if it were public:
use Obj;
my $obj = Obj->new();
print $obj->{foo};
For perhaps a more pleasant OOP in Perl, look at the Moose package which gives you more flexibility.
As #user2864740 pointed you don't need "OO" in perl to share variables.It is one way, Let's say you have two files
Foo.pm(package):
#!/usr/bin/perl
use strict;
use warnings;
use Exporter;
package Foo;
our #ISA = qw(Exporter);
our #EXPORT = qw( blat); #exported by default
our #EXPORT_OK = qw(bar );#not exported by default
our $x="42";#variable to be shared should be "our" not "my"
sub bar {
print "Hello $_[0]\n"
}
sub blat {
print "World $_[0]\n"
}
1;
Access that variable from other file as
bar.pl :
#!/usr/bin/perl
use strict;
use warnings;
use Foo;
print "$Foo::x";#imported variable
blat("hello");#imported subroutine
If you want to import listed functions then:
#!/usr/bin/perl
use strict;
use warnings;
use Foo qw(bar blat);# import listed subs
print "$Foo::x";#imported variable
blat("hello ");#imported subroutine
bar("hi");#this also get imported

Pass a subroutine to module and redefine it?

I'm trying to create a module with a method that receives a subroutine and redefines it. I had no problem redefining a subroutine inside the main script but the same syntax doesn't seem to work inside the method:
main.pl
use strict;
use warnings;
use ReDef;
sub orig{
print "Original!\n";
}
orig;
*orig=sub{print "not Original!\n";};
orig;
ReDef::redef(\&orig);
orig;
ReDef.pm
package ReDef;
use strict;
use warnings;
sub redef {
my $ref=shift;
*ref = sub {print "Redefined!";}
}
1;
Test output:
perl main.pl
Original!
Subroutine main::orig redefined at main.pl line 9.
not Original!
not Original!
ReDef::redef() doesn't redefine. The way I see it, the *ref is a coderef and assigning to it another subroutine should change main::orig();
What is the correct syntax?
Your redef function should be like this:
package ReDef;
use strict;
use warnings;
sub redef {
my $ref = shift;
no warnings qw(redefine);
*$ref = sub { print "Redefined!" };
}
And you should NOT call it like this:
ReDef::redef(\&orig);
Instead, you must call it like this:
ReDef::redef(\*orig);
Why? When you call orig, you're looking up the name "orig" via the symbol table, so the redef function needs to be altering the symbol table, so that it can point that name to a different bit of code. Globrefs are basically pointers to little bits of symbol table, so that's what you need to pass to ReDef::redef.
As an analogy, imagine that when you want to know the date of the Battle of Lewes, your procedure is to go to the library, look in the catalogue for the shelf address of a book on 13th century English battles, go to that shelf, and look up the date... voila 14 May 1264! Now, imagine I want to feed you altered information. Simply defining a new coderef would be like putting a new book on the shelf: it won't trick you because the catalogue is still pointing you at the old book. We need to alter the catalogue too.
UPDATE
You can make this a little prettier using prototypes. Prototypes are not usually recommended, but this seems to be a non-evil use for them...
use strict;
use warnings;
sub ReDef::redef (*) {
my $ref = shift;
no warnings qw(redefine);
*$ref = sub { print "Redefined!\n" };
}
sub orig { print "Original!\n" }
orig;
ReDef::redef *orig; # don't need the backslash any more
orig;
This works for me:
use v5.16;
use strict;
use warnings;
package Redef;
sub redef {
my $ref = shift;
${$ref} = sub { say "Redefined!"; }
}
package main;
my $orig = sub { say "Original!"; };
Redef::redef(\$orig);
$orig->(); # Redefined!
Although it’s just a result of trial and error, I’d be happy to see better answers.
What maybe got you confused is the typeglob operator, *. In Perl you dereference using a sigil (${$scalar_ref}, #{$array_ref}) and the * operator is used for symbol table tricks – which could also be used in your case, see the answer by #tobyink.

How to call method in base class in Perl

#child.pm
#!/usr/bin/perl
package child1;
use strict;
use warnings;
use Exporter;
use parent;
my #ISA=qw(cal Exporter);
sub new{
my $class=shift;
my $ref=cal->new();
bless ($ref,$class);
return $ref;
}
sub add{
my $ref=shift;
print "This is from child class";
my($a,$b)=#_;
return ($a+$b);
}
##parent.pm
#!/usr/bin/perl
package cal;
use strict;
use warnings;
use Exporter;
my #EXPORT=qw(add);
my #ISA=qw(Exporter EXPORT);
sub new{
my $class=shift;
my $ref=[];
bless ($ref,$class);
return $ref;
}
sub add{
my $ref=shift;
my $a=shift;
my $b=shift;
return ($a+$b);
}
1;
#test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Exporter;
use child;
my #ISA=qw(child1 Exporter);
my $obj=new child1();
my $sum=$obj->add(1,2);
print "$sum=sum";
I am getting the error Can't locate object method "add" via package "child1" at ./test.pl line 8.
I want to access the base class add method
and I am getting this above error
please clarify..
The main culprit here is my #ISA. For inheritance to work, you have to use the package #ISA (declare it with our).
However, there are some issues in your code beyond that:
Please use parent 'cal' instead of manipulating #ISA yourself.
Object-oriented modules have little reason to use Exporter.
The child1's new can be written without reblessing, because the parent's new is inherited. The inherited new is written in a way that already supports inheritance.
Don't give your modules lower-case names, these are reserved for “pragmas”. The parent module already exists, and I used it in my 1st point.
#ISA must be a public package variable, not a private lexical (my). Same for #EXPORT. Change my to our on all those declarations.
Even better, depending on the version of perl you have, simplify you life with either the parent or base pragma to load superclasses and to set up the class relationships.
With respect to style, you will avoid considerable confusion if you make the paths to the files that contains your modules’ code match their package names. You would do well to heed a well-established convention described in the perlmod documentation.
Module names are also capitalized unless they're functioning as pragmas; pragmas are in effect compiler directives, and are sometimes called “pragmatic modules” (or even “pragmata” if you're a classicist).
The Cal module uses an internal _initialize method as described in the perlobj documentation to facilitate inheritance of the constructor.
See below for a complete working example.
Cal.pm
package Cal;
use strict;
use warnings;
sub new {
my $class=shift;
my $self=[];
bless ($self,$class);
$self->_initialize();
return $self;
}
sub _initialize {}
sub add {
my $ref=shift;
my $a=shift;
my $b=shift;
print "This is from parent class\n";
return ($a+$b);
}
1;
Child1.pm
package Child1;
use warnings;
use strict;
use v5.10.1; # when parent was added to the core
use parent "Cal";
# if you have an older perl, use base instead of parent
# use base "Cal";
sub _initialize {
my $self=shift;
push #$self, "I am a " . ref($self) . "!";
}
sub add{
my $self=shift;
my($a,$b)=#_;
print "This is from child class\n";
return ($a+$b);
}
1;
test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Child1;
my $obj=Child1->new();
my $sum1=$obj->add(1,2);
print "$sum1=sum1\n";
# call the add method in Cal
my $sum2=$obj->Cal::add(1,2);
print "$sum2=sum2\n";
# call add as a class method of Cal, which
# happens to work in this case because Cal::add
# does not use the instance passed to it
my $sum3=Cal->add(1,2);
print "$sum3=sum3\n";
Output:
This is from child class
3=sum1
This is from parent class
3=sum2
This is from parent class
3=sum3
The .pm modules do not need, and likely do not want, the #!/usr/bin/perl lines. This is only for programs intended to be executed from the command line, like your .pl module. While you may 'perl -cw' your .pm modules, or do other command line debugging with them, such is not normal "production" use.
And .pl modules should not have #ISA, or other "our" declarations found in packages nor any exporter related things.
As said before, change "my"s for package stuff to "our"s. The "my" hides things to outsiders as if the statements were never there.
In the "cal" class, do you want something like the following? I favor SUPER as it really shows what is going on and is much more generic.
package child1;
use Exporter;
our #ISA=qw(cal Exporter);
sub new{
my $class=shift;
my $ref=$class->SUPER::new();
return $ref;
}
}
One final point: you may need a "\n" on the final print statement. Without it the buffer may not flush correctly on exit in some environments so you will never see the output.

Is there a standard way to selectively inherit methods from a Perl superclass?

Or: Is there a standard way to create subclass but make certain methods from the superclass yield a "Can't locate object method" error when called?
For example, if My::Foo inherits from My::Bar, and My::Bar has a method called dostuff, calling Foo->new->dostuff would die with the "Can't locate object method" error in some non-contrived/hackish way.
If the superclass is a Moose class you could use remove_method.
package My::Foo;
use Moose;
extends 'My::Bar';
# some code here
my $method_name = 'method_to_remove';
__PACKAGE__->meta->remove_method($method_name);
1;
This is documented in Class::MOP::Class and should work with MooseX::NonMoose but i am not sure.
You can create dummy methods in your child class that intercept the method calls and die.
package My::Foo;
our #ISA = 'My::Bar';
use Carp ();
for my $method qw(dostuff ...) {
no strict 'refs';
*$method = sub {Carp::croak "no method '$method' on '$_[0]'"};
}
You could even write a module to do this:
package No::Method;
use Carp ();
sub import {
my $class = shift;
my $caller = caller;
for my $method (#_) {
no strict 'refs';
*{"$caller\::$method"} = sub {
Carp::croak "no method '$method' on '$_[0]'"
};
}
}
And then to use it:
package My::Foo;
our #ISA = 'My::Bar';
use No::Method qw(dostuff);
This depends entirely on the way My::Bar and My::Foo are constructed. If they are your modules you may want to look into Exporter.
You can also import select functions from a class like so:
use POSIX qw{setsid};

How can I override a parent class function with child one in Perl?

I would like to replace parent function (Somefunc) in child class, so when I call Main procedure it should fail.
Is it possible in Perl?
Code:
package Test;
use strict;
use warnings;
sub Main()
{
SomeFunc() or die "Somefunc returned 0";
}
sub SomeFunc()
{
return 1;
}
package Test2;
use strict;
use warnings;
our #ISA = ("Test");
sub SomeFunc()
{
return 0;
}
package main;
Test2->Main();
When you call Test2->Main(), the package name is passed as the first parameter to the called function. You can use the parameter to address the right function.
sub Main
{
my ($class) = #_;
$class->SomeFunc() or die "Somefunc returned 0";
}
In this example, $class will be "Test2", so you will call Test2->SomeFunc(). Even better solution would be to use instances (i.e., bless the object in Test::new, use $self instead of $class). And even better would be to use Moose, which solves a lot of problems with object-oriented programming in Perl.
In order for inheritance to work you need to call your functions as methods, either on a class or an object, by using the -> operator. You seem to have figured this out for your call to Test2->Main(), but all methods that you want to behave in an OO way must be called this way.
package Test;
use strict;
use warnings;
sub Main
{
my $class = shift;
$class->SomeFunc() or die "Somefunc returned 0";
}
sub SomeFunc
{
return 1;
}
package Test2;
our #ISA = ("Test");
sub SomeFunc
{
return 0;
}
package main;
Test2->Main();
See perlboot for a gentle introduction and perltoot for more details.
Also, don't put parens after your subroutine names when you declare them -- it doesn't do what you think.