Is it possible to access static methods and attributes outside the class in Raku? - class

In raku it seems possible to define static methods (via sub keyword) and static attributes (via my) Those can be referenced inside the same class.
However, is it possible to access those methods and attributes outside of the class?
Something similar to this:
class MyClass {
my $attribute = 123;
sub my-method {
say 'Hello';
}
}
MyClass.$attribute;
MyClass.my-method;

it seems possible to define static methods (via sub keyword) and static attributes (via my) Those can be referenced inside the same class.
I can see why you're calling them static methods and attributes but Raku has a much simpler solution for those:
class MyClass {
method my-method {
say 'Hello';
}
method attribute is rw {
state $attribute = 123
}
}
say MyClass.attribute; # 123
MyClass.attribute = 99;
say MyClass.attribute; # 99
MyClass.my-method; # Hello
You could use our subs and our variables. our is the declarator used to define a lexical that is also for use outside the package it's declared withing. (mys are never shared; a sub declarator without an our is the same as my sub.)
So:
class MyClass {
our sub my-sub {
say 'Hello';
}
our $attribute = 123
}
import MyClass;
say $MyClass::attribute; # 123
$MyClass::attribute = 99;
say $MyClass::attribute; # 99
MyClass::my-sub; # Hello
As you can see, these aren't methods; this approach ignores OOP in the sense the prior solution does not.

Related

Return a base-class object from a derived-class object

I'm aware that Perl is not statically typed when I want to apply this mechanism to a Perl object of a derived class:
Say I have a base class B and a derived class D inheriting from B.
Also I have an object $obj that holds a D object.
A function Bf() is expecting a parameter of type B.
Obviously (by the rules of polymorphism) I can pass $obj to Bf() like Bf($obj), but unlike to a static-typed language Bf() will see the whole D object (and not just the elements of B).
Is there a (rather clean and simple) solution for this problem in Perl? The solution should "hide" the attributes (and methods) a B does not have from D in Bf(), not restricting modifications of the original B (which is D actually).
Adult Programmers only (added 2020-03-06)
OK, people wanted a more concrete description.
Unfortunately (as pointed out) the original program is highly complex and uses reflection-like mechanisms to generate getters, setters and formatters automatically, to I really can't give a minimum working example here, because it would not be minimal.
First I have a class MessageHandler that handle messages (no surprise!).
Then I have a function log_message($$$) that expects (among others) a MessageHandler object as first argument.
Then I have this hierarchy of classes (it's much more complex in reality):
MessageHandler
ControlMessageHandler (ISA: MessageHandler)
ControlMessageResponseHandler (ISA: ControlMessageHandler)
Now if log_message wants a MessageHandler I can pass a ControlMessageResponseHandler as it conforms to MessageHandler.
But doing so exposes all the attributes of ControlMessageResponseHandler to log_message that are non-existent in MessageHandler.
The danger is that log_message might (by mistake) access an attribute of ControlMessageResponseHandler that is not present in MessageHandler. To prevent errors I'd like to prevent that, or at least get some warning (like I would get in a statically-typed language as Eiffel).
Dirty Details inside
Just in case it matters, I'll sketch how my array objects are built (a lot of extra code would be needed for a working example):
First the array indices are allocated automatically like this:
use constant I_VERBOSITY => IS_NEXT->(); # verbosity level
use constant I_TAG => IS_NEXT->(); # additional tag
use constant I_TAG_STACK => IS_NEXT->(); # tag stack
use constant I_MSG_DEBUG => IS_NEXT->(); # handler for debug messages
...
use constant I_LAST => IS_LAST->(); # last index (must be last)
I_LAST is needed for inheritance.
The attributes are defines like this:
use constant ATTRIBUTES => (
['verbosity', I_VERBOSITY, undef],
['tag', I_TAG, \&Class::_format_string],
['tag_stack', I_TAG_STACK, undef],
['msg_debug', I_MSG_DEBUG, \&Class::_format_code],
...
);
The definition contains a hint how to format each attribute.
This information is used to set up formatters to format each attribute like this:
use constant FORMATTERS =>
(map { Class::_attribute_string($_->[0], $_->[1], undef, $_->[2]) }
ATTRIBUTES); # attribute formatters
Getters and setters are automatically defined like this:
BEGIN {
foreach (ATTRIBUTES) {
Class::_assign_gs_ai(__PACKAGE__, $_->[0], $_->[1]);
}
}
The constructor would use the following lines:
my $self = [];
$#$self = I_LAST;
$self->[I_VERBOSITY] = $verbosity;
...
And finally my object print routine goes like this:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, map { $_->($self, $a_sep) } FORMATTERS);
}
With inheritance it looks like this:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, $self->SUPER::as_string(),
map { $_->($self, $a_sep) } FORMATTERS);
}
I'm not sure what your problem is, although I think you took the long way to say "I have a function that expects a B object, and I want to pass it a D object."
If you only want objects of a certain exact type, don't accept anything else:
use Carp qw(croak);
sub Bf {
croak "Bad object! I only like B" unless ref $_[0] eq 'B';
...
}
But, that's a bad idea. A derived class should be just as good as the base class. The clean solution is to not care what type you get.
sub Bf {
croak "Bad object! Doesn't respond to foo!" unless $_[0]->can('foo');
...
}
Since this Bf method works with the base class, why would it look for something in some derived class it didn't know about? If the derived class has changed the interface and no longer acts like its parent, then maybe it's isn't a good fit for inheritance. There are many problems like this that are solved by a different architecture.
I think you'll have to come up with a concrete example where the derived class wouldn't work.
It sounds like for some reason you need your D object to behave like a B object, but at the same time not like a D object. As the existing answers and comments indicate, it's a very common to use a sub-class where the base class is expected, and most algorithms shouldn't care whether what you actually passed is D or B. The only reason I can think of why you would want otherwise is that D overrides (redefines) some methods in an incompatible way, and you want the methods from B instead.
package Dog;
sub new {
my ($class, %args) = #_;
return bless \%args, $class;
}
sub bark { print "Bark!\n"; }
package Dingo;
use parent 'Dog';
sub bark { print "...\n"; }
package main;
my $dingo = Dingo->new;
$dingo->bark; # "..."
(n.b., I've left off the recommended use strict; and use warnings; for terseness, they should be used in all packages)
You may be aware from reading perldoc perlootut and perldoc perlobj that an object in Perl is just a blessed reference of some sort; in the example above, we use a hash reference. If you are trying to get the "attributes" that only exist in B, I think you would have to write some sort of translation method. But, if you care about the methods that exist in B, all you have to do is re-bless it into the parent class.
my $dingo = Dingo->new;
$dingo->bark; # "..."
bless $dingo, "Dog";
$dingo->bark; # "Bark!"
Note that bless does not return a new reference, but modifies that reference in-place; if you want it to behave like a Dingo again, you have to bless it back.
Perhaps more conveniently you can define a method to create a copy for you and bless it into the appropriate class:
package Dog;
sub as_dog {
my ($self) = #_;
# The {} below create a shallow copy, i.e., a new reference
return bless { %{$self} }, __PACKAGE__;
}
#...
package main;
my $dingo = Dingo->new;
$dingo->bark; # ...
$dingo->as_dog->bark; # Bark!
$dingo->bark; # ...
While there doesn't seem to be a perfect solution, temporary "re-blessing" the object seems to get quite close to what is asked for:
sub Bf($) # expects a "B" object (or descendant of "B" (like "D"))
{
my $B = shift;
my $type = ref($B); # save original type
die "unexpected type $type" unless ($B->isa('B'));
bless $B, 'B'; # restrict to "B"'s features
$B->whatever(...);
#...
bless $B, $type; # restore original type
}

Perl class attribute inheritance

I have class attribute, e. g., counter of created objects in some base class,
package A;
our $counter = Counter->new; # not just a counter in fact, so initialization code should be inherited by descendants as well
sub new {
$counter++;
bless {}
}
sub get_counter {
$counter
}
package B;
use base 'A';
package main;
B->get_counter();
I want package B to have his own copy of this class attribute (e. g., counting objects of B class only), and all inherited methods from package A should deal with this copy. What is the correct way to implement this in plain perl and in Moo/Moose? Seems like MooX::ClassAttribute can not be inherited.
One ugly solution found is to repeat attribute initialization code in each descendant and use symbolic dereference like ${"${class}::counter"} in ancestor's methods to access this attribute with actual package name. But seems like there should be more elegant way.
The default Perl object model has no concept of class attributes. And there's no kind of hook like “when a new subclass is created, run this code”.
Instead, the base class could maintain a hash of counters, using the class name as keys:
package A;
my %counters;
sub new {
my ($class) = #_;
my $counter = $counters{$class} //= Counter->new;
$counter++;
return bless {} => $class;
}
sub get_counter {
my ($self_or_class) = #_;
my $class = (ref $self_or_class) || $self_or_class;
$counters{$class};
}
package B;
use parent -norequire, 'A';
This will create a new counter when an instance of a subclass is created. Note that the first argument to a method is either the class name or the object instance. We need to use that in new() as the hash key. In get_counter() I've written this in a way that the method can be called on both a class and an object to the same effect.
A similar technique is known as inside-out objects, where store object fields in a hash held by the class, so that the object itself doesn't contain any data.
(Why parent instead of base? The parent module only does inheritance, whereas base also integrates with the fields pragma which you should not use.)

When and why would you use a class with no data members?

I have noticed some Perl modules use a class based structure, but don't manage any data. The class is simply used to access the methods within and nothing more.
Consider the following example:
Class.pm
package Class;
use Moose;
sub do_something {
print "Hi!\n";
}
1;
test.pl
use Class;
# Instantiate an object from the class
my $obj = Class->new();
$obj->do_something();
In this example you can see that you would first instantiate an instance of the class, then call the method from the created object.
The same end result can be achieved like so:
Module.pm
package Module;
use strict;
use warnings;
sub do_something {
print "Hi!\n";
}
1;
test.pl
use Module;
Module::do_something();
I am wondering why people write modules using the first approach, and if there is some benefit that it provides. To me it seems like it adds an extra step, because in order to use the methods, you first need to instantiate an object of the class.
I don't understand why people would program like this unless it has some benefit that I am not seeing.
One benefit is inheritance. You can subclass behavior of an existing class if it supports the -> style subroutine calls (which is a weaker statement than saying the class is object-oriented, as I said in a comment above).
package Class;
sub new { bless \__PACKAGE__,__PACKAGE__ }
sub do_something { "foo" }
sub do_something_else { 42 }
1;
package Subclass;
#Sublcass::ISA = qw(Class);
sub new { bless \__PACKAGE__,__PACKAGE__ }
sub do_something_else { 19 }
package main;
use feature 'say';
$o1 = Class->new;
$o2 = Subclass->new;
say $o1->do_something; # foo
say $o2->do_something; # foo
say $o1->do_something_else; # 42
say $o2->do_something_else; # 19
A prominent use of this technique is the UNIVERSAL class, that all blessed references implicitly subclass. The methods defined in the UNIVERSAL namespace generally take a package name as the first argument (or resolve a reference in the first argument to its package name), are return some package information. The DB class also does something like this (though the DB package also maintains plenty of state).

How to use instance of class in same class Perl

I'm new to OOPerl, and wanted to know how I can reference an instance of a class within that class (i.e. $this in PHP) so that I'm able to call its "private" methods
To make it more clear:
in PHP for instance:
class Foo {
public function __construct(){
}
public function doThis(){
$that = $this->doThat(); //How to reference a "private" function in perl that is defined in the same class as the calling function?
return $that;
}
private function doThat(){
return "Hi";
}
}
Perl methods are ordinary subroutines that expect the first element of their parameter array #_ to be the object on which the method is called.
An object defined as
my $object = Class->new
can then be used to call a method, like this
$object->method('p1', 'p2')
The customary name is $self, and within the method you assign it as an ordinary variable, like this
sub method {
my $self = shift;
my ($p1, $p2) = #_;
# Do stuff with $self according to $p1 and $p2
}
Because the shift removes the object from #_, all that is left are the explicit parameters to the method call, which are copied to the local parameter variables.
There are ways to make inaccessible private methods in Perl, but the vast majority of code simply trusts the calling code to do the right thing.

Is there a way to know the methods of an instance of an unknown class in Perl

I have a program in Perl that uses a package that I got from another source. One of the functions of the method returns an object of an unknown class, Is there a way for me to get all the possible methods of an object without looking at its class implementation?
Not really.
TL;DR:
You can find the names of subroutines explicitly declared or placed into the object's class's namespace.
You can NOT distinguish which of these subroutines are object methods on your object, and which are class or non-object subs (this is the most serious problem/limintation among those listed).
You can NOT find the methods inherited by an object in the subclass from the superclass using this method, unless they were already called on your object.
This can be coded around, by either inspecting #ISA of the class to build up inheritance trees, or using on of proper CPAN modules.
You can NOT find the methods that are dynamically added to the class (AUTOLOAD, manual method injection in the code somewhere).
In detail
You can find all of the subroutines in that class (by combining the fact that the class namespace is a hash so all identifiers in it are keys in that hash; and the UNIVERSAL::can call to separate subroutines).
Therefore, if you are GUARANTEED (by non-technical contract) that 100% of subroutines in the class are object methods, AND that your class is NOT a subclass, you can find their list.
package MyClass;
use vars qw($z5);
my $x = 11; our $y = 12; $z5 = 14; %z2 = (1=>2); # my, our, globals, hash
sub new { return bless({}, $_[0]) }; # Constructor
sub x1 { my $self = shift; print $_[0]; };
sub y2 { my $self = shift; print $_[0]; };
##############################################################################
package MySubClass;
use vars qw(#ISA);
#ISA = ("MyClass");
sub z3 { return "" };
##############################################################################
package main;
use strict; use warnings;
my $obj = MyClass->new();
list_object_methods($obj);
my $obj2 = MySubClass->new();
list_object_methods($obj2);
$obj2->x1();
list_object_methods($obj2); # Add "x1" to the list!
sub list_object_methods {
my $obj = shift;
my $class_name = ref($obj);
no strict;
my #identifiers = keys %{"${class_name}::"};
use strict;
my #subroutines = grep { UNIVERSAL::can($obj, $_) } #identifiers;
print "Class: ${class_name}\n";
print "Subroutines: \n=========\n"
. join("\n", sort #subroutines) . "\n=========\n";
}
... prints:
Class: MyClass
Subroutines:
=========
new
x1
y2
=========
Class: MySubClass
Subroutines:
=========
new
z3
=========
Class: MySubClass
Subroutines:
=========
new
x1
z3
=========
Please note that the first-time list (for MySubClass) printed new and z3 but NOT x1 or y2 - because new was executed and z3 was declared in the class; but x1 and y2 was neither - they were merely theoretically inherited. BUT, once we executed an inherited x1 method, then the second-time list included it, while still missing inherited y2.
But you can NOT, unfortunately, distinguish a subroutine that is an object method (e.g. treats the first argument it gets as an object), a class method (e.g. treats the first argument it gets as a class name) or a non-OO sub (treats first argument as regular argument).
To distinguish between the 3, the ONLY way is to actually semantically analyze the code. Otherwise, you can't tell the difference between:
sub s_print_obj {
my ($self, $arg1) = #_;
$s->{arg1} = $arg1;
print "$arg1\n";
}
# $obj->s_print_obj("XYZ") prints "XYZ" and stores the data in the object
sub s_print_class {
my ($class, $arg1) = #_;
print "Class: $class\n";
print "$arg1\n";
}
# $obj->s_print_class("XYZ") prints "Class: MyClass\nXYZ\n"
sub s_print_static {
my ($self, $arg1) = #_;
print "$arg1\n";
}
# $obj->s_print_static("XYZ") prints stringified representation of $obj
NOTE: As a matter of fact, some people actually write their class's methods - those that CAN work this way - to explicitly work in ALL 3 (or first 2) cases, no matter how the method is called.
DVK's answer is accurate, but a bit lengthy. The short answer is yes you can, but you won't know what was intended as a public object method and what wasn't. Private methods and functions imported from other modules may show up.
Simplest way to get the list of callable, concrete (ie. non-AUTOLOAD) methods is to use the perl5i meta object's methods() method.
use perl5i::2;
my $object = Something::Something->new;
my #methods = $object->mo->methods;
That at least eliminates a lot of code.