Anonymous objects as easily as hashrefs?

revdiablo on 2009-09-15T22:44:15

Is there anything buried in the sea of Moose--or any object system on CPAN--that allows creating anonymous objects about as easily as creating anonymous hashrefs? I would be much happier using tiny objects to store structured data in my code, but the hashref syntax is just so darn convenient.

Here's an example of what I'd love to be able to do:

while (my $line = readline()) {
    my ($ding, $dong) = parse($line);

    next if $seen{$ding}++;

    push @dingfirsts, Object::Anonymous->new(
        ding => $ding,
        dong => $dong,
        line => $.,
    );
}

for (@dingfirsts) {
    say "First ", $_->ding, " on line ", $_->line;
}

Please ignore the obvious flaws in this contrived example. The most important part, to me, is that the object is defined by its constructor, and nowhere else. Methods would get created for any attributes given in the constructor.

Here's a quick implementation that may help explain what I'm after. There are some really obvious flaws that would prevent me from ever actually using this code--most significantly, it's modifying the Anonyject class at runtime, so it really isn't anonymous at all--but the mechanics are pretty close to what I'd want.

package Anonyject;

sub new {
    my $name = shift;
    my %args = @_;
    my $self = {};

    for my $arg (keys %args) {
        # Store the given value
        $self->{$arg} = $args{$arg};

        # Create the accessor
        my $methname = __PACKAGE__ . "::" . $arg;
        *{$methname} = sub {
            my ($innerself) = @_;
            return $innerself->{$arg};
        };
    }

    bless $self, $name;
}

package main;
use feature 'say';

my $obj1 = Anonyject->new(
    ding => "wing",
    dong => "wong",
    line => 23_939,
);
my $obj2 = Anonyject->new(
    something => "else",
);

say $obj1->ding;      # Says "wing"
say $obj2->something; # Says "else"

say $obj1->something; # Should break, but doesn't
say $obj1->broken;    # Breaks properly

So that's wishful thinking of the day. I'm hoping one of the popular object systems already does what I want, but maybe I can use Class::MOP to implement this properly. Any ideas would be lovely.


Class::MOP Makes This Easy

chromatic on 2009-09-15T23:12:30

You should be able to do this in a few minutes with Class::MOP::Class, especially the create_anon_class method.

Badger::Class also makes this easy

abw on 2009-09-16T08:41:43

The code sample you posted is almost there. You just need to create an anonymous package name instead of using __PACKAGE__. I would simply append the accessor names to the base class, e.g. Object::Anonymous_ding_wong_line

    sub new {
        my $class = shift;
        my %args = @_;
        my $self = { };
        my $name = join('_', $class, keys %args);

        for my $arg (keys %args) {
            # Store the given value
            $self->{$arg} = $args{$arg};

            # Create the accessor
            my $methname = $name . "::" . $arg;
            *{$methname} = sub {
                my ($innerself) = @_;
                return $innerself->{$arg};
            };
        }

        bless $self, $name;
    }

You can use Class::MOP::Class as chromatic notes. Or here's a similar thing using Badger::Class:

    package Class::Auto;

    use Badger;
    use Badger::Class 'class';

    sub new {
        my ($class, %self) = @_;
        my $name = join('_', $class, sort keys %self);
        class($name)->accessors(keys %self);
        bless \%self, $name;
    }

And here's an example of use:

    use Class::Anon;

    my $object = Class::Anon->new(
        x => 10,
        y => 20,
        z => 30
    );

    print ref $object, "\n"; # Class::Anon_x_y_z
    print $object->x, "\n"; # 10
    print $object->y, "\n"; # 20
    print $object->z, "\n"; # 30

Re:Badger::Class also makes this easy

abw on 2009-09-16T08:45:32

s/Auto/Anon/ # cut and paste fail

Re:Badger::Class also makes this easy

revdiablo on 2009-09-16T21:04:56

The code sample you posted is almost there. You just need to create an anonymous package name instead

Ha! Who woulda' thunk? I wrote that example code off-the-cuff in an attempt to better explain what I wanted. But that's a simple enough change that I'm now tempted to start using it as-is. Any other issues you can think might be lurking in the code?

Re:Badger::Class also makes this easy

revdiablo on 2009-09-16T21:06:49

I guess some sanity checks on the attribute names would be a good idea...

Data::AsObject

pshangov on 2009-09-16T09:49:03

Maybe this can help: http://search.cpan.org/dist/Data-AsObject/ (Note - I am the author).

And also

draegtun on 2009-09-16T12:16:33

Also check out:

* http://search.cpan.org/dist/Hash-AsObject/
* http://search.cpan.org/dist/Squatting/



And also:



* http://transfixedbutnotdead.com/2009/09/16/easy-anonymous-objects/



As always there is more than one way|solution|module to do it ;-)

/I3az/

Re:And also

draegtun on 2009-09-16T12:17:34

Opps that should be Squatting::H

http://search.cpan.org/dist/Squatting/lib/Squatting/H.pm