Why doesn't a subclass inherit its parent's constants? - perl

So I was going about my Moosey business and I thought hey might be nice to use a constant in these places where I'm using numbers, to make it clear what these numbers mean or in case they change later
So in the parent class I added the standard 'use constant'
package Parent;
use constant {
NO_LEVEL => 0,
MY_LEVEL => 1,
YOUR_LEVEL => 2,
};
package Child;
extends 'Parent';
#just to demonstrate that child can or cannot access the constant
sub printMyLevel{
print MY_LEVEL;
}
but the child class is not aware of the constants set in the parent! doh!
I'm guessing I have to do some Moose magic to get this to work right, or something else entirely. My searching on this issue didnt pull up any results =/

Constants are subroutines.
{
package Parent;
use Moose;
use namespace::autoclean;
use constant {
NO_LEVEL => 0,
MY_LEVEL => 1,
YOUR_LEVEL => 2,
};
__PACKAGE__->meta->make_immutable;
};
{
package Child;
use Moose;
use namespace::autoclean;
extends 'Parent';
sub printMyLevel {
my $self = shift;
my $class = ref $self;
print $class->MY_LEVEL;
}
__PACKAGE__->meta->make_immutable;
}
package main;
my $child = Child->new;
$child->printMyLevel;
Keep in mind that constants are subroutines with an empty prototype. perl takes advantage of this to inline them during compilation. However, method calls disregard prototypes, and therefore inheritable constants accessed this way would not be inlined.

This is actually mentioned in the documentation, if only in passing:
"Constants belong to the package they are defined in. To refer to a constant defined in another package, specify the full package name, as in Some::Package::CONSTANT. Constants may be exported by modules, and may also be called as either class or instance methods, that is, as Some::Package->CONSTANT or as $obj->CONSTANT where $obj is an instance of Some::Package. Subclasses may define their own constants to override those in their base class."

Since the constants are subroutines and you can get inheritance by calling them as methods bit has been covered to death already, here is a different spin on things.
If you know you are only working in a single file, you can use lexical constants to bridge packages:
package Parent;
our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL);
*NO_LEVEL = \0; # this split declaration installs aliases to numbers
*MY_LEVEL = \1; # into the lexicals. since numbers are constants
*YOUR_LEVEL = \2; # to perl, the aliased names are also constants
package Child;
# just to demonstrate that anything below can access the constants
sub printAll {
print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";
}
Child->printAll; # 0 1 2
eval {$NO_LEVEL = 3} or print "error: $#\n";
# error: Modification of a read-only value attempted at ...
If you don't need perl to die when assigning to the constant, the our declaration gets a bit simpler (and could be a my):
our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
You can bring back the constant nature while still using the terse syntax with a little magic:
my $constant = sub {Internals::SvREADONLY($_[$_], 1) for 0 .. $#_};
package Parent;
$constant->(our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2));
package Child;
# just to demonstrate that anything below can access the constants
sub printAll {
print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n"; # interpolates :)
}
Child->printAll; # 0 1 2
eval {$NO_LEVEL = 3} or print "error: $#\n";
# error: Modification of a read-only value attempted at ...
You can of course omit the $constant coderef and inline the magic:
package Parent;
Internals::SvREADONLY($_, 1)
for our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);

Call them as a method.
sub printMyLevel{
my ( $self, ) = $_;
print $self->MY_LEVEL;
}

Inheritance affects methods calls ($x->m), period.

Related

What's the correct way to deal with multiple inheritence of modules sharing a common "ancestor" in Perl?

(The Moose/Moo answer is, of course, "Roles". This question is about the general case where you want to combine two modules that are both subclasses of the same parent, assuming no Moose/Moo.)
Let's take a slightly contrived example: the modules LWP::UserAgent::Determined and LWP::RobotUA are both subclasses of LWP::UserAgent and extend it in different ways. What should I do if I want to create an object that combines the methods from both? It will, at its core, still be a LWP::UserAgent object, and the other two modules don't clash with each other, so it should be easy, right?
As far as I can tell, the correct thing to do is to create a new package which declares both of the other two as parents – use parent qw(LWP::RobotUA LWP::UserAgent::Determined) – and then create objects from that. And, indeed, if you do that, you get an object that contains the methods from both, as well as from the base class LWP::UserAgent, and almost everything works as you'd expect.
But not quite. Both LWP::UserAgent::Determined and LWP::RobotUA have default values for certain attributes that are set when the object is created if no other value is given. When combining the two, LWP::RobotUA's defaults get set, but not LWP::UserAgent::Determined's. So something must be wrong.
Here's some test code:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.016;
use LWP::RobotUA;
use LWP::UserAgent::Determined;
package MyUA;
use parent qw(LWP::RobotUA LWP::UserAgent::Determined);
for my $module (qw(LWP::RobotUA LWP::UserAgent::Determined MyUA)) {
say '# ', $module, ' #';
my $ua = $module->new(
'agent' => 'Test-UA',
'from' => 'example#example.com',
);
my $req = HTTP::Request->new(GET => 'https://www.bbc.co.uk/emp/network_status.txt');
my $response = $ua->request($req);
unless ($module eq 'LWP::UserAgent::Determined') {
say 'Use sleep? : ', $ua->use_sleep() // 'not defined!';
say 'Allowed OK? : ', $ua->rules->allowed('https://www.bbc.co.uk/') // 'not defined!';
say 'Sites with rules: ', (defined $ua->rules()->{loc}) ? join(', ', (sort keys %{$ua->rules()->{loc}})) : 'not defined!';
}
unless ($module eq 'LWP::RobotUA') {
print 'Timings: ';
if (defined $ua->timing()) {
say $ua->timing();
}
else {
print 'Timing defaults not set! ';
$ua->timing('1,5,10,20,60,240');
say '...but the method works: ', $ua->timing();
}
say 'Retry codes: ', (defined $ua->codes_to_determinate()) ? join(', ', (sort keys %{$ua->codes_to_determinate()})) : 'not defined!';
}
say '#'x60;
}
This outputs:
# LWP::RobotUA #
Use sleep? : 1
Allowed OK? : 1
Sites with rules: www.bbc.co.uk:443
############################################################
# LWP::UserAgent::Determined #
Timings: 1,3,15
Retry codes: 408, 500, 502, 503, 504
############################################################
# MyUA #
Use sleep? : 1
Allowed OK? : 1
Sites with rules: www.bbc.co.uk:443
Timings: Timing defaults not set! ...but the method works: 1,5,10,20,60,240
Retry codes: not defined!
############################################################
Here you can see that the methods for both modules work, but default values are not set for LWP::UserAgent::Determined's timing() or codes_to_determinate() methods when combined with LWP::RobotUA, while LWP::RobotUA's use_sleep() method is created with its default value of 1. Setting values manually works fine, however, and otherwise the combined object works as expected.
So, in summary: what's the correct way of handling this case, where you want to combine two modules that subclass a common third? Is this, in fact, correct, but I just chose an unfortunate example and LWP::UserAgent::Determined isn't well-behaved in how it sets it's defaults?
Your new class effectively looks like this:
package MyUA;
use parent qw(LWP::RobotUA LWP::UserAgent::Determined);
1;
Let's test it like this:
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use MyUA;
my $ua = MyUA->new(
agent => 'test',
from => 'me#example.com',
);
say ref $ua;
That tells us that we have "MyUA" object. But what is it really? What have we made?
Well, objects are build using a constructor method. That's (usually) called new(). In this case, you haven't defined a new() method in your class. So Perl will look for the method in the superclasses. It does that by searching the classes it finds in #INC and seeing if each one, in turn, contains a new() method.
Both of your superclasses have a new() method. But Perl only needs one. So when it finds one, it stops looking and calls that method. The first one it calls is the one in LWP::RobotUA (because that's the first one on the list passed to use parent). So that one gets called.
This means that what you've actually got here is an object of class LWP::RobotUA. Well, mostly. It's been blessed into the right class and if you call any methods that are in LWP::UserAgent::Determined, but not in LWP::RobotUA, it will still work. But none of the LWP::UserAgent::Determined initialisation code has been called.
And that's a pretty good demonstration of why multiple inheritance is a bad idea. It's so hard to get it right in all but the most trivial of cases.
I can't give you an answer here. Because only you know which bits of the two superclasses you need. But the solution will involve adding your own new() method to your class and probably calling the two superclass constructors from within that.
Update: Ok, I've had a closer look. And it might be easier than I thought.
LWP::RobotUA::new() makes a call to LWP::UserAgent::new() in the middle of doing various other things. But LWP::UserAgent::Determined::new() makes a call to LWP::UserAgent::new() at the start of its processing and then helpfully bundles all of its other initialisation up in a separate method called _determined_init().
So it looks like your solution could be a simple as adding a constructor method like this:
sub new {
my $class = shift;
my $self = $class->SUPER::new(#_);
$self->_determined_init();
return $self;
}
The call to $class->SUPER::new() calls LWP::RobotUA::new() because that's the first class in #INC. That, in turn, calls LWP::UserAgent::new() - so that initialisation is all done. We then just have to call _determined_init() in order to initialise the other superclass.
It seems to work in my (very basic) testing. But I'm still very dubious about multiple inheritance :-)
Update 2: Yes. ikegami is right. My solution only fixes the problems with constructing the object. I didn't look into actually using it.
That can't work without changes to LWP::UA::Determined and/or LWP::RobotUA.
But those changes can be applied through monkey patching. A solution is presented at the bottom.
The Problem
LWP::UA::Determined wraps ->simple_request, which is to say it provides a ->simple_request which calls ->SUPER::simple_request (which is ->LWP::UA::simple_request)
LWP::RobotUA wraps ->simple_request, which is to say it provides a ->simple_request which calls ->SUPER::simple_request (which is ->LWP::UA::simple_request)
When you call ->request, you'll end up calling ->simple_request. Depending on the order of the modules in #ISA, this will call either LWP::UA::Determined::simple_request (and then LWP::UA::simple_request) or LWP::RobotUA::simple_request (and then LWP::UA::simple_request).
It will not call both LWP::UA::Determined::simple_request and LWP::RobotUA::simple_request. And it wouldn't work if it did because that would end up calling LWP::UA::simple_request twice.
The exact same problem exists for ->new.
Demo
use feature qw( say );
{ package Base;
sub method { say __PACKAGE__; } }
{ package SubA; our #ISA = qw( Base );
sub method { say __PACKAGE__; $_[0]->SUPER::method(); } }
{ package SubB; our #ISA = qw( Base );
sub method { say __PACKAGE__; $_[0]->SUPER::method(); } }
{ package SubAB; our #ISA = qw( SubA SubB ); }
SubAB->method(); # SubA Base
The Solution
LWP::UA::Determined and LWP::RobotUA would need to use next::method instead of SUPER (or they would need to be designed entirely differently) for this to even have a chance of working.
Demo
use feature qw( say );
{ package Base;
sub method { say __PACKAGE__; } }
{ package SubA; use mro 'c3'; our #ISA = qw( Base );
sub method { say __PACKAGE__; $_[0]->next::method(); } }
{ package SubB; use mro 'c3'; our #ISA = qw( Base );
sub method { say __PACKAGE__; $_[0]->next::method(); } }
{ package SubAB; use mro 'c3'; our #ISA = qw( SubA SubB ); }
SubAB->method(); # SubA SubB Base
The Workaround
All hope isn't lost! We can monkey patch LWP::UA::Determined and LWP::RobotUA to get the desired behaviour!
Simply add this to your program:
use LWP::RobotUA qw( );
BEGIN {
package LWP::RobotUA::Inserted;
use mro 'c3';
our #ISA = #LWP::RobotUA::ISA;
sub new { return $_[0]->next::method(); }
sub simple_request { return $_[0]->next::method(); }
sub agent { return $_[0]->next::method(); }
#LWP::RobotUA::ISA = __PACKAGE__;
}
use LWP::UserAgent::Determined qw( );
BEGIN {
package LWP::UserAgent::Determined::Inserted;
use mro 'c3';
our #ISA = #LWP::UserAgent::Determined::ISA;
sub new { return $_[0]->next::method(); }
sub simple_request { return $_[0]->next::method(); }
#LWP::UserAgent::Determined::ISA = __PACKAGE__;
}
You also need use mro 'c3'; in your own class (or choose the order of #ISA carefully) for ->agent to work correctly.
Demo
# Insert code from first demo here.
{ package SubA::Inserted; use mro 'c3'; our #ISA = #SubA::ISA;
sub method { return $_[0]->next::method(); }
#SubA::ISA = __PACKAGE__; }
{ package SubB::Inserted; use mro 'c3'; our #ISA = #SubB::ISA;
sub method { return $_[0]->next::method(); }
#SubB::ISA = __PACKAGE__; }
SubAB->method(); # SubA SubB Base

Accessing class variables in inherited function?

I'm trying to create child classes in Perl that inherit class functions from a single parent. I got it to partially work, using the object method syntax Child->inheritedMethod() to call inherited functions outside the child, and my $class=shift; $class->inheritedMethod(); inside the child class, as described here.
However, for inherited methods, it seems control is passed to parent class, and the method is run in the parent scope with the parent variables. For example, this is in the Parent class:
our $VERSION = 0.11;
our $NICKNAME = "Parent Base";
sub version{ $VERSION }
sub whoami{ $NICKNAME }
sub whereami{
my $class = shift;
print "should be printing whereami right now...\n";
print "## In ",(caller(1))[3]," of ",$class->whoami," ",$class->version," in ",__PACKAGE__,"\n";
}
Each child class declares its own $VERSION and $NICKNAME, which I hoped would be accessed in place of the parent variables. But when I call whereami from the child, it gives
## Child::Method of Parent Base 0.11 in Parent.
Questions:
Is there a way around this? Some other module I should use like Moo(se)? Export all the methods instead of inheritance, which I hear shouldn't be done (polluting the namespace, not a problem here)?
Would this still be an issue using objects and object
attributes/variables? I'm trying to avoid it due to my team's
aversion to object-oriented.
Is this how inheritance usually works,
or just Perl? I thought the method would be called within the scope
of the child class, not passed to the parent.
The problem is that the method accesses the variable from the lexical scope where it was declared, i.e. the parent class. Class variables are therefore not the same thing as class attributes.
You can access the correct variable by fully qualifying its name (not possible under strict refs:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
our $package = 'Parent';
sub get_package {
my $class = shift;
{ no strict 'refs';
return (caller(0))[3], $class, ${"$class\::package"}
}
}
}
{ package Son;
use parent 'Parent';
our $package = 'Son';
}
print join ' ', 'Son'->get_package, "\n";
print join ' ', 'Parent'->get_package, "\n";
In Moo*, you can use Moo*X::ClassAttribute:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
use Moo;
use MooX::ClassAttribute;
class_has package => (is => 'ro',
default => 'Parent');
sub get_package {
my $class = shift;
return $class->package;
}
}
{ package Son;
use Moo;
use MooX::ClassAttribute;
extends 'Parent';
class_has package => (is => 'ro',
default => 'Son');
}
print 'Parent'->get_package, "\n";
print 'Son'->get_package, "\n";
Note that MooX::ClassAttribute says
Overriding class attributes and their accessors in subclasses is not yet supported.
Unlike in Moose, you can't use the class_has '+package' => (default => 'Son'); syntax for overriding.

Multiple data members in a perl class

I am new to perl and still learning oop in perl. I usually code in C, C++. It is required to bless an object to notify perl to search for methods in that package first. That's what bless does. And then every function call made with help of -> passes the instance itself as first parameter. Now I have a doubt in writing the constructor for a new object. Normally a constructor would normally look like:
sub new {
my %hash = {};
return bless {%hash}; #will automatically take this package as the class
}
Now I want to have two data members in my class so I can do something like this:
sub new {
my %hash = {};
$hash->{"table_header"} = shift #_; #add element to hash
$hash->{"body_content"} = shift #_;
return bless {%hash}; #will automatically take this package as the class
}
My question is that is this the only possible way. Can't we have multiple data members like in C and C++ and we do have to use strings like "table_header" and "body_content".
EDIT:
In C or C++ we can directly reference the data member(assume its public for now). Here there is one extra reference which has to be made. I wanted to know if there is any way we can have a C like object.
sub new {
my $table_header = shift #_;
my $body_content = shift #_;
#bless somehow
}
Hope this clears some confusion.
There are modules that make OOP in Perl easier. The most important is Moose:
use strict; use warnings;
package SomeObject;
use Moose; # this is now a Moose class
# declare some members. Note that everything is "public"
has table_header => (
is => 'ro', # read-only access
);
has body_content => (
is => 'rw', # read-write access
);
# a "new" method is autogenerated
# some method that uses these fields.
# Note that the members can only be accessed via methods.
# This guards against typos that can't be easily caught with hashes.
sub display {
my ($self) = #_;
my $underline = "=" x (length $self->table_header);
return $self->table_header . "\n" . $underline . "\n\n" . $self->body_content . "\n";
}
package main;
# the "new" takes keyword arguments
my $instance = SomeObject->new(
table_header => "This is a header",
body_content => "Some body content",
);
$instance->body_content("Different content"); # set a member
print $instance->display;
# This is a header
# ================
#
# Different content
If you get to know Moose, you will find an object system that is far more flexible than that in Java or C++, as it takes ideas from Perl6 and the Common Lisp Object System. Of course, this is fairly ugly, but it works well in practice.
Because of the way Perl OOP works, it isn't possible to have the instance members accessible as variables on their own. Well, almost. There is the experimental mop module which does exactly that.
use strict; use warnings;
use mop;
class SomeObject {
# Instance variables start with $!..., and behave like ordinary variables
# If you make them externally accessible with "is ro" or "is rw", then
# appropriate accessor methods are additionally generated.
# a private member with public read-only accessor,
# which has to be initialized in the constructor.
has $!table_header is ro = die 'Please specify a "table_header"!';
# a private member with public read-write accessor,
# which is optional.
has $!body_content is rw = "";
# new is autogenerated, as in Moose
method display() {
# arguments are handled automatically, so we could also do $self->table_header.
my $underline = "=" x (length $!table_header);
return "$!table_header\n$underline\n\n$!body_content\n";
}
}
# as seen in Moose
my $instance = SomeObject->new(
table_header => "This is a header",
body_content => "Some body content",
);
$instance->body_content("Different content"); # set a member, as in Moose
print $instance->display;
# This is a header
# ================
#
# Different content
Although it has pretty syntax, don't use mop right now for serious projects and stick to Moose instead. If Moose is too heavyweight for you, then you might enjoy lighter alternatives like Mouse or Moo (these three object systems are mostly compatible with each other).
You are getting confused between hashes and hash references. You are also forgetting that the first parameter to any method is the object reference or the name of the package. Perl constructors are inherited like any other method, so you must bless the new object into the correct package for polymorphism to work properly. This code is what you intended
sub new {
my $package = shift;
my %self;
$self{table_header} = shift;
$self{body_content} = shift;
bless \%self, $package;
}
I am not clear what you mean by “directly reference the data member”, but if you hoped that you could avoid writing $self everywhere so that every variable was implicitly an element of the hash then you cannot. Perl is far more flexible than most languages, and can use any blessed reference as an object instance. It is most common to use a hash, but occasionally a reference to an array, a scalar, or even a file handle is more appropriate. The cost of this flexibility is specifying exactly when you are referring to a member of the blessed hash. I don't see that it's too great a burden.
You can always write your code more concisely. The method above can be written
sub new {
my $package = shift;
my %self;
#self{qw/ table_header body_content /} = #_;
bless \%self, $package;
}

Why is constant not automatically inherited?

As we know constant in Perl is just sub,
but why are they not inherited?
As the matter of fact, they are:
use strict; use warnings;
package Father;
use constant CONST => 1;
package Child;
use base 'Father';
sub new { bless {}, shift }
package main;
my $c = Child->new;
print $c->CONST; # 1
print CONST(); # undefined subroutine
Methods are inherited, functions are not. If you want to inherit the constant, you'll need to call it like a method.
$self->FOO
or
__PACAKAGE__->FOO
That said, you should be importing constants, not inheriting them.

How can I create internal (private) Moose object variables (attributes)?

I would like some attributes (perhaps this is the wrong term in this context) to be private, that is, only internal for the object use - can't be read or written from the outside.
For example, think of some internal variable that counts the number of times any of a set of methods was called.
Where and how should I define such a variable?
The Moose::Manual::Attributes shows the following way to create private attributes:
has '_genetic_code' => (
is => 'ro',
lazy => 1,
builder => '_build_genetic_code',
init_arg => undef,
);
Setting init_arg means this attribute cannot be set at the constructor. Make it a rw or add writer if you need to update it.
/I3az/
You can try something like this:
has 'call_counter' => (
is => 'ro',
writer => '_set_call_counter',
);
is => 'ro' makes the attribute read only. Moose generates a getter. Your methods will use the getter for incrementing the value, like so:
sub called {
my $self = shift;
$self->_set_call_counter( $self->call_counter + 1 );
...
}
writer => '_set_call_counter' generates a setter named _set_call_counter. Moose does not support true private attributes. Outside code can, technically, call _set_call_counter. By convention, though, applications do not call methods beginning with an underscore.
I think you want MooseX::Privacy.
The perldoc tells you all you should need - it adds a new trait to your attributes allowing you to declare them as private or protected:
has config => (
is => 'rw',
isa => 'Some::Config',
traits => [qw/Private/],
);
I haven't been able to figure out a way to make Moose attributes completely private. Whenever I use has 'name' => (...); to create an attribute, it is always exposed to reading at a minimum. For items I want to be truly private, I'm using standard "my" variables inside the Moose package. For a quick example, take the following module "CountingObject.pm".
package CountingObject;
use Moose;
my $cntr = 0;
sub add_one { $cntr++; }
sub get_count { return $cntr; }
1;
Scripts that use that module have no direct access to the $cntr variable. They must use the "add_one" and "get_count" methods which act as an interface to the outside world. For example:
#!/usr/bin/perl
### Call and create
use CountingObject;
my $co = CountingObject->new();
### This works: prints 0
printf( "%s\n", $co->get_count() );
### This works to update $cntr through the method
for (1..10) { $co->add_one(); }
### This works: prints 10
printf( "%s\n", $co->get_count() );
### Direct access won't work. These would fail:
# say $cntr;
# say $co->cntr;
I'm new to Moose, but as far as I can tell, this approach provides completely private variables.
Alan W. Smith provided a private class variable with a lexical variable, but it is shared by all objects in the class. Try adding a new object to the end of the example script:
my $c1 = CountingObject->new();
printf( "%s\n", $c1->get_count() );
# also shows a count of 10, same as $co
Using MooseX:Privacy is a good answer, though if you can't, you can borrow a trick from the inside-out object camp:
package CountingObject;
use Moose;
my %cntr;
sub BUILD { my $self = shift; $cntr{$self} = 0 }
sub add_one { my $self = shift; $cntr{$self}++; }
sub get_count { my $self = shift; return $cntr{$self}; }
1;
With that, each object's counter is stored as an entry in a lexical hash. The above can be implemented a little more tersely thus:
package CountingObject;
use Moose;
my %cntr;
sub add_one { $cntr{$_[0]}++ }
sub get_count { return $cntr{$_[0]}||0 }
1;