[Phoenix-pm] inside out objects

Scott Walters scott at illogics.org
Mon Nov 28 11:54:28 PST 2005


Ooops, forgot the code...

sleep 10;

for my $i (0..1000) {
    # eval qq{
    #     sub foo$i {
    #         my \$x = shift; \$x *= \$i; print "\$i \$x\n";
    #     }
    *{"foo$i"} = sub {
        my $x = shift; $x *= $i; print "\$i \$x\n";
    };
}

sleep 60;

That's the simple eval vs. closure test.

  package hashclosure;

  sub import {
      my $caller = caller;
      *{$caller.'::AUTOLOAD'} = sub {
          my $method = $AUTOLOAD; $method =~ s/.*:://;
          return if $method eq 'DESTROY';
          my $this = shift;
          local *{$caller.'::this'} = $this;
          if(! exists $this->{$method}) {
              my $super = "SUPER::$method";
              return $this->$super(@_);
          }
          $this->{$method}->(@_);
      };
  }
  1;

  package Person;
  use hashclosure;

  our $this;

  sub new {
      my $class = shift;
      my $name;
      my $age;
      bless {
          name => sub { $name },
          set_name => sub { $name = shift },
          age  => sub { $age },
          set_age => sub { $age = shift },
          print_stats => sub {
              print "name: ", $name, "\n";
              print "age: ", $age, "\n";
          },
          get_older => sub {
              $age++;
          },
      }, $class;
  }

sleep 5;

my $person = Person->new;

sleep 5;

my @people;
push @people, Person->new for 1..100000;

sleep 60;


... finally, the normal version...

  package Person;

  sub new {
      my $class = shift;
      bless { name => undef, age => undef }, $class;
  }

sub name { my $self = shift; $self->{name} }
sub set_name { my $self = shift; $self->{name} = shift }
sub age { my $self = shift; $self->{age} }
sub set_age { my $self = shift; $self->{age} = shift }
sub print_stats {
              my $self = shift;
              print "name: ", $self->{name}, "\n";
              print "age: ", $self->{age}, "\n";
          }
sub get_older {
              my $self = shift;
              $self->{age}++;
}

sleep 5;

my $person = Person->new;

sleep 5;

my @people;
push @people, Person->new for 1..100000;

sleep 10;



On  0, Michael Friedman <friedman at highwire.stanford.edu> wrote:
> Scott,
> 
> Thanks! I'm curious, though, about the memory footprint that using  
> hashclosure would have. Since each method becomes a closure, I assume  
> that means that each method would end up being instantiated in a  
> brand new space in memory for each new object you create. Thus, if  
> you had 10 objects, you'd have 10 different copies of each method as  
> well as 10 copies of the field values. In either the single-hash- 
> based object or the inside-out object, the methods are shared between  
> all instances of the class, so they only have to go into memory once.
> 
> So if you were using 100s of objects, that memory could be  
> problematic. :-)
> 
> Still, it's a cool way to avoid having to use object syntax within  
> the object itself.
> 
> The link I included originally is to a new module, Class::Std, which  
> is supposed to make using inside out objects much easier. I hope to  
> try it out later this week, so I'll let everyone know how it goes.
> 
> -- Mike
> 
> 
> On Nov 23, 2005, at 9:06 AM, Scott Walters wrote:
> 
> > Hi Michael,
> >
> > I thought it was a cute trick, but I'm surprised it's recommended.
> > What I do depends on the situation -- the client, the style of the  
> > code
> > I'm adding to, etc.
> >
> > When I don't necessarily care about encapsulation, I use an excellent
> > package by Juerd called Attribute::Property:
> >
> >        package Person;
> >        use Attribute::Property;
> >
> >        sub new  : New;
> >        sub name : Property;
> >        sub age  : Property { /^\d+\z/ and $_ > 0 }
> >
> >        sub print_stats {
> >            my $self = shift;
> >            print "name: ", $self->name, "\n";
> >            print "age: ", $self->age, "\n";
> >        }
> >
> >        sub get_older {
> >            my $self = shift;
> >            $self->age++;
> >        }
> >
> >        package main;
> >
> >        my $person = Person->new(name => 'Fred', age => 23);
> >        $person->get_older;
> >        $person->name = "Fred Worth";
> >        $person->print_stats;
> >
> > A::P creates new methods for you that initialize instance variables  
> > from
> > arguments (just like in Perl 6 -- hence _Perl 6 Now_ including  
> > discussion
> > of it), and it also creates lvalue accessors for the instance data
> > when you use the :Property attribute of subroutines. It's a lot less
> > code to write and the code looks a lot better.
> >
> > For stuff I use internally, when I want some ecapsulation and not  
> > to have to
> > shift $this, I often use a little package called hashclosure.pm.  
> > hashclosure
> > basically tells Perl that the object's methods are code references  
> > inside
> > of the hash. The hash contains methods and code references rather than
> > instance data. Instance data is "my" variables lexically closed  
> > over by the
> > methods.
> >
> >   package hashclosure;
> >
> >   sub import {
> >       my $caller = caller;
> >       *{$caller.'::AUTOLOAD'} = sub {
> >           my $method = $AUTOLOAD; $method =~ s/.*:://;
> >           return if $method eq 'DESTROY';
> >           my $this = shift;
> >           local *{$caller.'::this'} = $this;
> >           if(! exists $this->{$method}) {
> >               my $super = "SUPER::$method";
> >               return $this->$super(@_);
> >           }
> >           $this->{$method}->(@_);
> >       };
> >   }
> >
> >   1;
> >
> > This is the AUTOLOAD glue needed to so that objects created as
> > follows work:
> >
> >   package Person;
> >   use hashclosure;
> >
> >   our $this;
> >
> >   sub new {
> >       my $class = shift;
> >       my $name;
> >       my $age;
> >       bless {
> >           name => sub { $name },
> >           set_name => sub { $name = shift },
> >           age  => sub { $age },
> >           set_age => sub { $age = shift },
> >           print_stats => sub {
> >               my $self = shift;
> >               print "name: ", $self->name, "\n";
> >               print "age: ", $self->age, "\n";
> >           },
> >           get_older => sub {
> >               my $self = shift;
> >               $self->age++;
> >           },
> >       }, $class;
> >   }
> >
> > Since the "instance data" is lexically closed over by the methods and
> > scoped to the new { } block, encapsulation is pretty good (PadWalker
> > and such can still get to it, but XS can do anything).
> >
> > Best of all, you don't have to write that annoying $self->{foo}  
> > crap --
> > just $foo will do. That's an improvement even over $self->foo as in  
> > A::P.
> >
> > The AUTOLOAD logic shifts $this for us and sticks it and puts it into
> > the $this defined by 'out $this'.
> >
> > Downsides are lack of lvalue accessors (so you can't do $person- 
> > >name = "Fred")
> > and the ugly initialization syntax.
> >
> > This might be workable with lvalue, but I'd have to do a much larger
> > and messier AUTOLOAD to make it happen and my first attempt didn't
> > pan out.
> >
> > At the last meeting, I got out my Object::Lexical on the projector and
> > shoved that in people's faces. It also makes instance data into
> > lexicals, but the implementation is completely different (it creates
> > a new stash for each object created, stuffs closures into it, and
> > blesses it -- it's probably the strangest thing I've ever done in  
> > Perl).
> > Here's what code using it looks like:
> >
> >   use Object::Lexical;
> >   use Sub::Lexical;
> >
> >   sub new {
> >
> >     our $this;
> >     my $name;
> >     my $age;
> >
> >     my sub age { $age };
> >     my sub name { $name };
> >
> >     sub print_stats {
> >         print "name: ", $name, "\n";
> >         print "age: ", $age, "\n";
> >     }
> >
> >     sub get_older {
> >         $age++;
> >     }
> >
> >     instance();
> >
> >   }
> >
> > It just doesn't get any more clear or concise than that for creating
> > objects in Perl. instance() serves the same purpose as bless(),
> > but it does the actual stash-blessing and closure-generating.
> > To get this pretty syntax, you need a source filter -- that's what
> > Sub::Lexical does. For some other idioms that don't use a source
> > filter, see http://search.cpan.org/~swalters/Object-Lexical-0.02/ 
> > Lexical.pm.
> > This module is a bit buggy, by the way, but if anyone actually
> > has any interest in it, I'll fix the thing.
> >
> > Regards,
> > -scott
> >
> > On  0, Michael Friedman <friedman at highwire.stanford.edu> wrote:
> >> So, I just read through _Perl Best Practices_ and it's fantastic.
> >> It's even well written. And I agree with almost all of Damian's
> >> recommendations for making more maintainable Perl code... all except
> >> inside out objects.
> >>
> >> However, as I've already had one tranformative religious experience
> >> from this book (I've changed my braces style), I'm willing to give
> >> the guy a chance on this one. But I need some more evidence.
> >>
> >> Has anyone used inside out objects before? I completely believe that
> >> they handle encapsulation much better, but what I don't buy is that
> >> they're actually easier to maintain and equivalently easy to
> >> understand as the "regular" hash-based objects are.
> >>
> >> So, anyone have real world advice on using them?
> >>
> >> -- Mike
> >>
> >> PS - For those not in the know, inside out objects are identified by
> >> a unique scalar that acts as an index into a list of hashes, one hash
> >> for each attribute/field of the object. The values are held in the
> >> set of hashes in a closure, so absolutely no one but the class itself
> >> can access them. There is an example in the code block on http://
> >> www.windley.com/archives/2005/08/best_practices.shtml, and other
> >> examples elsewhere that I can't seem to find at the moment. :-(
> >>
> >> ---------------------------------------------------------------------
> >> Michael Friedman                     HighWire Press
> >> Phone: 650-725-1974                  Stanford University
> >> FAX:   270-721-8034                  <friedman at highwire.stanford.edu>
> >> ---------------------------------------------------------------------
> >>
> >>
> >> _______________________________________________
> >> Phoenix-pm mailing list
> >> Phoenix-pm at pm.org
> >> http://mail.pm.org/mailman/listinfo/phoenix-pm
> 
> ---------------------------------------------------------------------
> Michael Friedman                     HighWire Press
> Phone: 650-725-1974                  Stanford University
> FAX:   270-721-8034                  <friedman at highwire.stanford.edu>
> ---------------------------------------------------------------------
> 
> 
> _______________________________________________
> Phoenix-pm mailing list
> Phoenix-pm at pm.org
> http://mail.pm.org/mailman/listinfo/phoenix-pm


More information about the Phoenix-pm mailing list