Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Basic Class question

by packetstormer (Monk)
on Aug 21, 2013 at 10:05 UTC ( #1050318=perlquestion: print w/ replies, xml ) Need Help??
packetstormer has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks

I have being using Perl for a while but only recently had any need to start looking at OOP. I notice a couple of odd things that I hope someone could clear up?

What is the reason for the first argument that is passed to the routine in a class being the class name? Is this useful in certain ways?

Secondly, I noticed that if I export my functions, from the class, and then, in my calling script call it by its name (e.g &function("data") ) then the first argument isn't the class name. However, if I call it directly e.g MyDir::Class->function("data"), from the same script, then the first argument is the class name.

Is there good reasons why this is the case, or am I doing something really silly!?

Comment on Basic Class question
Select or Download Code
Re: Basic Class question (perlootut)
by Anonymous Monk on Aug 21, 2013 at 10:12 UTC

    hope someone could clear up?

    perlootut, Modern Perl

    What is the reason for the first argument that is passed to the routine in a class being the class name?

    Implementation detail

    Is this useful in certain ways?

    Without this there is no OO

    Secondly,

    See above :)

    Is there good reasons why this is the case,

    See above, thats the way it is, the way it works

    or am I doing something really silly!?

    :) Somehow you managed to discover a lot without stumbling upon perlootut, http://modernperlbooks.com/books/modern_perl/index.html, or equivalent

      or am I doing something really silly!?

      See Path::Tiny, unless you're exporting a constructor like path(), you shouldn't be exporting anything :)

Re: Basic Class question
by tobyink (Abbot) on Aug 21, 2013 at 10:49 UTC

    "What is the reason for the first argument that is passed to the routine in a class being the class name? Is this useful in certain ways?"

    You need the class name so that you can call other methods from within your method. For example:

    { package Processor; sub process_one_thing { my $class = shift; my ($thing) = @_; print "Processing '$thing'...\n"; } sub process_many_things { my $class = shift; my (@things) = @_; for my $thing (@things) { # we need $class so we can call this # other method! $class->process_one_thing($thing); } } } Processor->process_many_things("Foo", "Bar", "Baz");

    "I noticed that if I export my functions, from the class, and then, in my calling script call it by its name (e.g &function("data") ) then the first argument isn't the class name"

    That's because you're not supposed to do that. Don't export methods *.

    Basically methods in Perl are just syntactic sugar. Foo->bar(...) is a shorthand for Foo::bar("Foo", ...), but with inheritance thrown in, so that if there is no such function Foo::bar(), the parent classes of Foo will be consulted.

    * it is sometimes possible to use exported functions and OO programming in the same package to good effect, but until you're comfortable using OO, don't try it.

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
Re: Basic Class question
by kcott (Abbot) on Aug 21, 2013 at 12:08 UTC

    G'day packetstormer,

    Take a look at this code:

    $ perl -Mstrict -Mwarnings -le ' package MyDir::Class; # A class method sub new { my ($class, $args_ref) = @_; return bless $args_ref => $class; } # An instance method sub id { my $self = shift; return $self->{id}; } package MyDir::Class::SubClass; use base qw{MyDir::Class}; # no methods: all functionality is inherited package main; # Create a MyDir::Class instance my $obj = MyDir::Class::->new({id => "X"}); print "Class object: ", $obj; print "Class Name: ", ref $obj; print "Object ID: ", $obj->id(); # Create a MyDir::Class::SubClass instance my $obj_sub = MyDir::Class::SubClass::->new({id => "Y"}); print "Class object: ", $obj_sub; print "Class Name: ", ref $obj_sub; print "Object ID: ", $obj_sub->id(); ' Class object: MyDir::Class=HASH(0x7ffeda002ee8) Class Name: MyDir::Class Object ID: X Class object: MyDir::Class::SubClass=HASH(0x7ffeda029cb0) Class Name: MyDir::Class::SubClass Object ID: Y

    There's a class called MyDir::Class. It has two methods. A class method (new()) which, as you've already noted, has the class name ($class) as its first argument; a common usage for the class name is shown: yes, it's useful. An instance method (id()) which has an object (a class instance) as its first argument ($self); a common usage for the object is shown: again, it's useful. Note: there's no exporting involved!

    There's a second class called MyDir::Class::SubClass. It has no methods: all functionality is inherited. Note: there's no importing or exporting involved!

    Finally, there's the main package where objects are created (via new() — also called a constructor) and where those objects invoke methods. Note: there's no importing involved!

    I've written all that code on the command-line as a single, multi-line string. In a real world situation, the modules would be in separate *.pm files and the script (in yet another file) wouldn't have a package main; statement but would have use statements for the module(s) involved.

    I see you've already been provided with the perlootut - Object-Oriented Programming in Perl Tutorial link: that covers the basics. Also take a look at perlobj - Perl object reference and particularly its Invoking Class Methods section which has information specific to your question.

    You haven't shown the code where you're exporting functions. While you may have a valid reason to do this, you typically don't: use inheritance instead of export/import mechanisms.

    I see you wrote "... &function("data") ...". Don't prepend an ampersand ("&") to your function calls unless you have a specific reason for doing this and understand what that reason is. In general, this isn't what you want and could potentially have unexpected side-effects: see perlsub - Perl subroutines for details. For an OO context, take note of "&NAME(LIST); # Circumvent prototypes." and, further down in the Prototypes section, "Method calls are not influenced by prototypes ...".

    -- Ken

      Ken,

      Thanks for this, I see I have a bit of reading to do, which is fine (until now I have figured most things out myself but that won't be an option from now on I think!)

      One small question: how come the id method isn't expecting the class name as it's first argument? Is it only the first sub that does this? (I will read up anyway, but that question stood out from your code)

      Thanks again.

        One small question: how come the id method isn't expecting the class name as it's first argument?

        Class methods expect class names (string)

        Instance methods expect objects (blessed reference)

        "One small question: how come the id method isn't expecting the class name as it's first argument? Is it only the first sub that does this? (I will read up anyway, but that question stood out from your code)"

        If you look at the usage of the id() method (which is an instance method), you'll see INSTANCE->METHOD() (e.g. $obj->id()). INSTANCE ($obj in that example) is the first argument to METHOD().

        If it needs to, an instance method can determine the class of the object with ref (as shown with "print "Class Name:   ", ref $obj;" in my original example).

        Similarly, a class method has the form CLASS->METHOD() and the first argument is CLASS.

        The order in which instance and class methods are defined has no bearing on the type of method they are nor the arguments they receive. A class can have any number of methods: MyDir::Class has two (one of each); MyDir::Class::SubClass has no methods; some other class may have lots of instance methods but no class methods; and so on, there's no rules stipulating what the number or mix must be.

        In many cases, the first interaction you have with a class is to create an instance of that class (i.e. an object to work with). Accordingly, the constructor (a class method) is often the first method defined; however, being defined first doesn't bestow any special characteristics.

        [Veering off-topic a bit here but included to clarify a couple of misconceptions I've seen over the years. (1) Constructors are often called new() but they don't have to be. (2) A class can have more than one constructor. As an example of both of these points, take a look at PDF::API2 which has three constructors (new(), open() and openScalar()) all of which return PDF::API2 objects.]

        I've added two more methods to the MyDir::Class example to demonstrate all of the above. get_instance_count() is a class method that's not special at all: it gets a class as its sole argument and uses it to generate the return value. DESTROY() is an instance method that's a destructor: this is special; it's not something you'll normally need; perlobj: Destructors has details. I've also changed the order of the methods so you now have instance/class/instance/class: this was really just to make a point rather than suggesting some preferred ordering. Also note that while the three methods in the anonymous block needed to be grouped within that block, their order within the block is not important.

        $ perl -Mstrict -Mwarnings -le ' package MyDir::Class; # An instance method sub id { my $self = shift; return $self->{id}; } # Anonymous block: code outside this block cannot see %instance_co +unt { my %instance_count; # Constructor: a class method sub new { my ($class, $args_ref) = @_; ++$instance_count{$class}; return bless $args_ref => $class; } # Destructor: an instance method sub DESTROY { my $self = shift; --$instance_count{ref $self}; return; } # Another class method sub get_instance_count { my $class = shift; return $instance_count{$class} || 0; } } package MyDir::Class::SubClass; use base qw{MyDir::Class}; # no methods: all functionality is inherited package main; sub print_all_instance_counts { print "MyDir::Class instances: ", MyDir::Class::->get_instance_count(); print "MyDir::Class::SubClass instances: ", MyDir::Class::SubClass::->get_instance_count(); } # Starting counts print "*** Counts before any objects are created ***"; print_all_instance_counts(); # Create several objects my @objs = map { MyDir::Class::->new({id => $_}) } "A" .. "D"; my @obj_subs = map { MyDir::Class::SubClass::->new({id => $_}) } " +W" .. "Z"; print "*** Counts after initial objects are created ***"; print_all_instance_counts(); # Destroy some objects undef $objs[-1]; pop @obj_subs; splice @obj_subs, 1, 1; shift @obj_subs; print "*** Counts after some objects are destroyed ***"; print_all_instance_counts(); ' *** Counts before any objects are created *** MyDir::Class instances: 0 MyDir::Class::SubClass instances: 0 *** Counts after initial objects are created *** MyDir::Class instances: 4 MyDir::Class::SubClass instances: 4 *** Counts after some objects are destroyed *** MyDir::Class instances: 3 MyDir::Class::SubClass instances: 1

        -- Ken

Re: Basic Class question
by dsheroh (Parson) on Aug 21, 2013 at 12:13 UTC
    Yes, there's a good reason for it: That's how OO works in Perl. When you invoke a sub using ->, then whatever is on the left side of the arrow is passed as the first argument.

    If you call My::Class->foo, then foo gets My::Class as its first argument, allowing it to know which class it was called as a method of. This isn't terribly important in most cases, but, without it, things like polymorphic constructors get a lot trickier, since they don't know whether they were called on the base class or a subclass.

    If you call $some_obj->foo, then foo gets $some_obj as its first argument, which is extremely important if you want to be able to access the object's instance data.

    If you call My::Class::foo, then it gets only the arguments you explicitly provide, the same as if you imported it and then called foo()1. But you probably don't really want to do that. Writing subs that can be called in both OO and non-OO fashions adds complexity and, in most cases, provides little or no benefit.


    1 Note that I didn't prefix the function call with &. It's not needed in Perl 5 and has non-obvious side-effects which you almost certainly don't need. Only use the & prefix on function calls if you know exactly what the effects are and why you want those effects.

Re: Basic Class question
by sundialsvc4 (Monsignor) on Aug 21, 2013 at 15:08 UTC

    In some ways, OOP was “grafted onto” Perl, particularly by means of the verb, bless, which sets a “blessed” flag and associates the object with a “class (equals package ...) name,” so that when you use the method-call (->) syntax, Perl knows what to do.   Most other programming languages “hide” the mechanics of “how OOP actually works” far more thoroughly than Perl does, and if you cut your teeth in one of those camps it will seem quite strange.   Yet, this is by design.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1050318]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2014-09-16 16:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (36 votes), past polls