[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