Perl Shorthand Instance Variables

perl

Short Hand Instance Variables

Synopsis

Typing $me->{'foo'} to get to your "foo" instance variable is verbose and unnatural. Factor out the extra syntax for accessing instance variables over static variables.

When

Whenever you use instance variables in your classes, which should be often.

Discussion

This page has been eclipsed by Object::Lexical, but is still a good illustration of the problem being solved, and munging namespaces in Perl.

Other Object Oriented languages don't distinguish between instance variables and global variables in syntax, only definition. Perl does. This translates to more typing.

# java:

  public void mouseDown(Event e, int x, int y) {
    lastx = x; lasty = y;
    return true;
  }

  # perl:

  sub mouseDown {
    my($this, $event, $x, $y) = @_;
    $this->{'lastx'} = $x;
    $this->{'lasty'} = $y;
    return 1;
  }

$this->{'lastx'} is hardly a worthy substitute for being able to merely say $lastx and have the language remember the fact that $lastx is an instance variable. This requirement is going a long way towards making our little program ugly. For longer functions, we can make some symbol table aliases to allow us to access things inside of a hash or array using a short name:

  sub mouseDown {

    my($this, $event, $x, $y) = @_;

    local *lastx = \$this->{'lastx'};
    local *lasty = \$this->{'lasty'};

    # now we can refer to $this->{'lastx'} merely as $lastx, just like Java and C++!

    $lastx = $x;
    $lasty = $y;

  }

This just makes the problem worse for shorter functions, cluttering them with unneeded syntax. As a compromise, you can use this trick for large functions where instance variables are referenced over and over again, and for short functions, use the plain old hash dereference syntax that you already know and tolerate. Don't like those options? Me neither. Lets see what else we can dig up:

package Yawn;

  sub public { 
    local $this = shift;
    my $coderef = pop;
    my @fields = keys %$this;
    my $field;
    FIELD:
      $field = pop @fields;
      local *{$field};
      *{$field} = \$this->{$field};
    goto FIELD if(@fields);
    $coderef->(@_);
  }

  sub private {
    caller(1) eq __PACKAGE__ or 
      die sprintf "Cannot invoke private method %s from outside %s", 
        (caller(1))[3], __PACKAGE__;
    public(@_);
  }

Including these methods at the top of your class will allow you to abbreviate method classes:

sub set_x {
    private @_, sub {
      $x = shift;
    };
  }

Call public() or private() with the arguments and a subroutine to execute. public() will run the passed subroutine unconditionally, while private() will only run it if your method was invoked from within the object. If another object or package tries to invoke your private method directly, it will generate an error message:

Cannot invoke private method Yawn::setb_x from outside Yawn at foo.pl line 17.

Additionally, you don't need to say $me->{'x'} to get at your "x" field: you can refer to it directly as $x. For each value in the object hash, a local temporary alias is set up with the same name. $this is set to $me in the same way, which is similar to what other languages provide.

Even this is tedious: you have to repeat code at the top of each class, and the syntax for declaring an accessor isn't natural. You could of course use require to read it in from a module that doesn't use package to change the namespace, in the Perl 4 fashion. I like local customizations, and if I wanted to take the above code and start melding it, that's exactly what I would do. I've done that and gone one step further: I've put it into a regular module and grafted some logic onto it that avoids having to use any strange syntax at all in your modules. The program [XXX attached, available on website, whatever] Implicit This.pm gives you all of these features:

package Foo;

  use Implicit This;
  Implicit This::imply;

  sub new {
    my $type = shift;
    my %options = @_;
    my $me = { x => $options{'x'}, y => $options{'y'}};
    bless $me, $type;
  }

  sub _set_x {
    $x = $shift;
  }

  sub get_x {
    return $x;
  }

Implicit This::imply() is called separately because Implicit This.pm needs to wait until your package finishes loading before taking action. When imply() is run, it looks through your name table, finds all of the functions, and puts a thin wrapper around them. The wrapper creates temporary aliases to all of the instance variables, once again giving them the same name.

Since the "public" and "private" keywords are gone now, the convention of using functions starting with underscores as private functions is used. If a function starts with an underscore, Implicit This.pm checks the call stack using caller() to make sure that the call originated from within the package.

This isn't a real Design Pattern. It does make Perl objects more usable and natural, so I'm giving it special attention.

Implicit This was one pass at a solution. Object::Lexical is the second pass.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License