Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Generic object in inside-out classes

by Anno (Deacon)
on Feb 10, 2007 at 20:50 UTC ( #599394=perlmeditation: print w/replies, xml ) Need Help??

With inside-out classes it can be arranged that the class itself behaves like an object, which I'll call its generic object. The generic object can be initialized and normal accessors can be used with it to set and retrieve values. It is, however, not a real object in the sense of a blessed reference. Different classes have (represent? are?) different generic objects.

What it amounts to is that every object method of such a class can also be used as a class method. Here is a minimal class that shows how to enable this feature.

The modification is in the function id() used to get the reference address of an object when accessing data.
package SomeClass; use Scalar::Util; # The traditional id() function is modified: for a non-ref, return # the argument, not undef sub id { Scalar::Util::refaddr( $_[ 0]) || shift } { # a field (attribute, object variable) with a read/write accessor my %some_field; sub some_field { my $obj = shift; $some_field{ id( $obj)} = shift if @_; $some_field{ id( $obj)}; } sub init { my $obj = shift; $some_field{ id( $obj)} = shift; $obj; } sub new { my $class = shift; bless( do{ \ my $o})->init( @_); } } package main; my $obj = SomeClass->new( 123); print $obj->some_field, "\n"; SomeClass->init( 456); print SomeClass->some_field, "\n";
This works, of course, because with this id() function the values of some_field for the generic object are stored in the same hash as the values for real objects, but keyed by the class name. Since the keys of real objects are integers in decimal, there is no conflict.

With an un-modified id() function all such accesses would be keyed by the empty string, independent of the class (probably spewing warnings). It might appear to "work", but would break down with subclassing.

There are probably not too many uses for the generic object, but two come to mind. For one, often a place is wanted to store default values for all objects. The generic object offers itself as a class-wide template to hold such values.

Another use would be in the implementation of singleton classes. Defining

sub new { my $class = shift; $class->init( @_); }
would make sure that no real object (blessed ref) is created. Only the generic object (the invioking class) is ever returned by new().

I could contrive ther uses, but I'll stop here. I feel the feature has a certain neat-appeal, applications are secondary :)


Replies are listed 'Best First'.
Re: Generic object in inside-out classes
by stvn (Monsignor) on Feb 11, 2007 at 15:27 UTC

    I think this has interesting possibilities for describing inside-out singletons, but it blurs the lines between class and instance-of-the-class a little too much IMO.

    This is similar actually to the Perl 6 concept of the prototypical object, where a variable which is typed as a certain class has access to that class's methods without being officially blessed into that class. This makes things like this possible.

    my Foo $foo .= new();
    The Foo::new method here is invoked on the $foo variable, which is (as @Larry describes it) "Foo but undef" which means that the variable will evaluate to undef in the right context, but act as the class Foo other contexts.

    However, your example does differ in that the Perl 6 version of the Foo class itself doesn't become an active instance as it would seem to do in your example.

    This also reminds me of metaclasses in a way. In most metaclass systems, a metaclass is an instance of the class "Class" and is responsible for facilitating the creation of instances of the class it descibes. Because each class is then essentially a metaclass instance, it is possible to store class data and methods as instance data and methods of the metaclass instance itself (of course a little syntactic sugar is usually required to make this seem more natural).

    But again, your example does differ from metaclasses in that the instance of a metaclass is not the same as an instance of the class it describes, so it cannot respond to the same methods or store the same data as the class.

Re: Generic object in inside-out classes
by xdg (Monsignor) on Feb 11, 2007 at 13:31 UTC

    That's an interesting technique -- I'm definitely adding it to my notes for the next time I give my inside-out objects talk.

    One negative that I see is that it adds even more overhead to every property access -- instead of id being an alias to refaddr, this approach has a second function call and a comparison.

    That means I'd never want to implement this unless it was really necessary. I certainly wouldn't use it for convenience to specify initializing values -- that's a high runtime cost for a one-time activity.


    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Scalar::Util could provide an id() function (say) with the variant behavior implemented in XS. That would reduce the overhead to nil.

      Alternatively, perl 5.10 will have Hash::Util::FieldHash to support inside-out classes. A class based on that will allow generic objects.


Re: Generic object in inside-out classes
by ysth (Canon) on Feb 11, 2007 at 06:27 UTC
    It feels very wrong to have a subclass have its own generic object.
      What would be the alternative? One program-wide generic object?
      sub id { Scalar::Util::refaddr( shift) || 'the_one_generic_object' }
      would do that. So would the standard id() function (ignoring possible "uninitialized" warnings) with an empty string instead of 'the_one_generic_object".

      I wouldn't put it in terms of "right" and "wrong", but obviously I expect the behavior that has one generic object per class to be more useful. I'd have to build, or at least sketch, some example applications that involve subclassing to see which behavior wins out in practice. At this point it's just my intuition, but I'm not alone in that. In his perltooc, Tom Christansen discusses a similar concept for the standard hash-based type of class. The functional equivalent of the generic object is what he calls the eponymous hash. That implementation also provides one such hash per class.


Re: Generic object in inside-out classes
by shmem (Chancellor) on Mar 19, 2007 at 12:04 UTC
    I would rather use init for class initialization, and new to create an instance. Besides that, fine meditation and good technique. Something clicked in my head... thanks.


    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://599394]
Approved by liverpole
Front-paged by Arunbear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (8)
As of 2021-01-18 12:04 GMT
Find Nodes?
    Voting Booth?