How perl calls a method

Andrew Fresh

I'm going to assume some basic knowledge of perl here, but the talk hits only a few of the messy bits. This talk is specifically perl5, but many concepts are hopefully helpful to other languages. I try to use very generic language and try to keep from any specific examples of objects even though many people like to use $fido isa Dog isa Animal and such things.

Methods are $object->method()

    use My::Package;

    my $object = My::Package->new();

    $object->method()

Perl didn't start out object oriented so it is a bit tacked on. This makes how they work possibly more visible than other languages.

Loads modules

    package My::Package;
    use v5.20;
    use warnings;
    use parent 'My::Parent';
    1;

use parent does two steps you used to do manually

    BEGIN {
        require My::Parent;
        push @ISA'My::Parent';
    }

Just the C<use parent 'My::Parent';> line. Your classes can have multiple parents, that's confusing, so we'll get there later. It does these things in a BEGIN block so it happens at a specific time That doesn't matter at all for this. BEGIN, UNITCHECK, CHECK, INIT and END It does a couple things we used to do manually. C<require My::Parent> and C<push @ISA, 'My::Parent'> The thing we care about is @ IS A but first something completely different

use parent vs use base

use parent is just a cleaner version of use base

In any case you care about parent is better.

Way off topic here base.pm grew too much cruft and they rewrote the useful bits in parent.pm to maintain backwards compatibility. If use parent works for you, you probably want to use that. If you know why you need to use base, then my opinion is useless.

require My::Parent

Converts My::Parent to a path

Which gives us My/Parent.pm

Then look in @INC to find the file.

A side track, sort of. Changes to a filename Looks in @INC

@INC and %INC

We now have My/Parent.pm

And perhaps @INC = ( '/foo', '/bar' );

First look at /foo/My/Parent.pm

Then /bar/My/Parent.pm

If found, reads in the file then sets $INC{'My/Parent.pm'} = '/bar/My/Parent.pm';

@INC and %INC are global

My/Parent.pm is a path to a file @INC is a list of directories to look in. Obviously /foo and /bar are made up, the next slide has a real list. %INC is a list of what we've found. There are other ways things can get into %INC so the filename doesn't always line up.

@INC on my machine

@INC = (
    '/usr/local/libdata/perl5/site_perl/amd64-openbsd',
    '/usr/libdata/perl5/site_perl/amd64-openbsd',
    '/usr/local/libdata/perl5/site_perl',
    '/usr/libdata/perl5/site_perl',
    '/usr/libdata/perl5/amd64-openbsd/5.20.2',
    '/usr/local/libdata/perl5/amd64-openbsd/5.20.2',
    '/usr/libdata/perl5',
    '/usr/local/libdata/perl5',
    '.',
)

The @INC on my machine came from perl -V You can manipulate this with $ENV{PERL5LIB}, like C<PERL5LIB=./lib:./t/lib>.

push @ISA, 'My::Parent';

Back to push @ISA, 'My::Parent'

@ISA is a list of package names we will look for methods in.

Our inheritance.

It's package scoped, actually @My::Package::ISA

Each package has one, like @My::Parent::ISA

In perl a Class is just a package that acts in a specific way. @ISA is a list of all the classes we inherit. It *can* contain subrefs, but that's out of scope. It's a package scoped variable, @My::Package::ISA Each package in perl has an ISA included. Some may be empty. But they all inherit automatically from UNIVERSAL

Finds the Class of $object

The first thing is to figure out what we're dealing with

    my $object = My::Package->new;
    my $class  = ref $object;

It's like $class = 'My::Package'.

You can figure this out, there are other ways, but an easy one is to ask what sort of reference $object is. Blessing?

bless $scalar, $class;

$object = My::Package->new makes $object->isa($class)

    package My::Package;
    use parent 'My::Parent';

    sub new {
        my ($class) = @_;
        return bless {}, $class;
    }

Another side track, blessing perldoc -f bless sets a bit saying this package is magical An anonymous hashref, could be any scalar value, but usually a hashref. Now perl knows C<< $object->isa($class) >> All we care about is that perl knows that C<< ref $object eq $class >>

Functions are in the symbol table

There is another magical hash, %My::Package::

This is the symbol table

It holds a list of things, like package variables

and what we're interested in, functions

For example $My::Package::{my_function}->*{CODE}

Which is also available as &My::Package::my_function

C<%My::Package::> It ends with double colons, weird, right? *not* an instance variable. functions are in C<< *{ $My::Package::{my_function} }{CODE} >> or as C<< &My::Package::my_function >> Using experimental feature 'postderef' for clarity perldoc perlmod / perldoc perlref Lots of confusing references and typeglobs and whatnot. Not particularly important, because perl looks it up for us. I am a bit confused on something as I expect C<delete $My::Package::{my_function}> to make C<< My::Package->my_function >> fail but it doesn't.

Look through @ISA

sub dfs_can ( $method$class = __PACKAGE__ ) {
    if ( my $code = \&{$class . '::' . $method} ) {
        return $code;
    }

    foreach my $parent (@$class . '::ISA' }) {
        my $code = dfs_can( $method$parent );
        return $code if $code;
    }

    die qq{Can't locate object method "$method" ...};
}

To make the code fit, I had to use experimental 'signatures'.

If perl doesn't find &My::Package::method

It then looks in @ISA, which happens to contain My::Parent

And we look again, for &My::Parent::method

First look for &My::Package::method Then search @ISA, checking &My::Parent::method This is the DFS search *{ ${$class . '::'}{$method} }{CODE} perldoc UNIVERSAL

mro inheritance, "dfs" vs "C3"

Method Resolution Order

Another side track, Method Resolution Order dfs (classic) vs C3 you don't hear about what C3 means very often because it's technobabble mro::get_linear_isa('My::Package'); perldoc mro

Classic diamond inheritance pattern

    package A;

    package B;
    use parent 'A';

    package C;
    use parent 'A';

    package D;
    use parent ('A''B');

Point at the slides!

What's wrong with dfs?

Classic diamond inheritance pattern

     <A>
    /   \
  <B>   <C>
    \   /
     <D>

dfs looks at D B A C

That means if C overrides a method in A, your class that uses that method will get the one from A, not the one you want from C.

What's right with C3

Classic diamond inheritance pattern

     <A>
    /   \
  <B>   <C>
    \   /
     <D>

C3 looks at D B C A

consistent with the extended precedence graph, local precedence order, and monotonicity aims to provide a sane method resolution order under multiple inheritance This essentially means that no class will appear before any of its subclasses.

Now call it as My::Package::method( $object )

Perl has found the function!

And we can call it with $object as the first argument.

    My::Package::method( $object );

Or maybe you found it with can

    my $method = $object->can('method');
    $method->( $object )

So call the fully qualified function with the first argument of $object. Which just gives the same reference we had before.

Moose, it must be totally different

Moose extends is pretty much the same as use parent

Moose with Roles are another matter

It all still works the same once it's constructed though.

Moose does all sorts of magic to construct special classes Creates packages out of thin air and modifies the symbol table But overall it's about the same

Finally

Load packages

Adjust @ISA with parent classes

Look in symbol tables for all classes in @ISA to find $method

Call $method->( $object, @other_args )

And that's it. We look thorough @ISA to find the method in the symbol table *{ ${$package . '::'}{$method} }{CODE} And then call it, putting the object as the first argument.

for more

perldoc perlobj

perldoc perlmod

perldoc UNIVERSAL

perldoc parent

perldoc mro

SUPER is important, but I didn't even mention it here 'method' was always a string when we called it *on* an object or class but it could be a variable containing a string or a code reference.

Thank you!

afresh1@openbsd.org
andrew@cpan.org
andrew@afresh1.com
afresh@grantstreet.com
@AFresh1 on twitter

http://cvs.afresh1.com/~andrew/talks