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

Outside-in objects with Object::LocalVars

by xdg (Monsignor)
on Jul 28, 2005 at 10:00 UTC ( #478896=perlmeditation: print w/ replies, xml ) Need Help??

The module I referenced in my earlier RFC has been released to CPAN as Object::LocalVars (with thanks to simonm for the name suggestion). I'm describing the technique I'm using as "outside-in" objects -- a word play on "inside-out" objects, as there are strong similarities (and one huge difference). It provides a very streamlined approach to coding objects in perl. An excerpt from the POD follows to describe this technique, including a list of features and drawbacks:

NAME

Object::LocalVars - Outside-in objects with local aliasing of $self and object variables

SYNOPSIS

  package My::Object;
  use strict;
  use Object::LocalVars;

  give_methods our $self;  # this exact line is required

  our $field1 : Prop;
  our $field2 : Prop;

  sub as_string : Method { 
    return "$self has properties '$field1' and '$field2'";
  }

DESCRIPTION

This is an early development release. Documentation is incomplete and the API may change. Do not use for production purposes. Comments appreciated.

This module helps developers create "outside-in" objects. Properties (and $self) are declared as package globals. Method calls are wrapped such that these globals take on a local value that is correct for the specific calling object and the duration of the method call. I.e. $self is locally aliased to the calling object and properties are locally aliased to the values of the properties for that object. The package globals themselves are empty and data are stored in a separate namespace for each package, keyed off the reference addresses of the objects.

"Outside-in" objects are similar to "inside-out" objects, which store data in a single lexical hash closure for each property that is keyed off the reference addresses of the objects. Both differ from "traditional" Perl objects, which store data for the object directly within a blessed reference to a data structure. For both "outside-in" and "inside-out" objects, data is stored centrally and the blessed reference is simply a key to look up the right data in the central data store.

Unlike with "inside-out" objects, the use of package variables for "outside-in" objects allows for the use of local symbol table manipulation. This allows Object::LocalVars to deliver a variety of features -- though with some drawbacks.

Features

  • Provides $self automatically to methods without my $self = shift and the like
  • Provides dynamic aliasing of properties within methods -- methods can access properties directly as variables without the overhead of calls to accessors or mutators, eliminating the overhead of these calls in methods
  • Array and hash properties may be accessed via direct dereference of a simple variable, allowing developers to push, pop, splice, etc. without the usual tortured syntax to dereference an accessor call
  • Properties no longer require accessors to have compile time syntax checking under use strict
  • Uses attributes to mark properties and methods, but only in the BEGIN phase so should be mod_perl friendly (though I haven't tested this yet)
  • Provides attributes for public, protected and private properties, class properties and methods
  • Does not use source filtering
  • Orthogonality -- can subclass just about any other class, regardless of implementation. (Also a nice feature of some "inside-out" implementations)

Drawbacks

  • Method efficiency -- wrappers around methods create extra overhead on method calls
  • Minimal encapsulation -- data is hidden but still publically accessible, unlike approaches that use lexical closures to create strong encapsulation
  • Designed for single inheritance only. Multiple inheritance may or may not work depending on the exact circumstances

At this point, it is fully functional, though not yet optimized. Early benchmarking indicates that it can be much faster than other object-oriented techniques in Perl if the number of property accesses within methods is high relative to the number of method calls or it can be slower if the ratio is the other way around. However, the real intention was to streamline development time, with less redundant (and error-prone) typing, so benchmarks are intended to suggest that not too much is lost (and some may even be gained) for the syntactic convenience that Object::LocalVars offers

I encourage any curious monks to experiment with it and post or send feedback, either on the technique itself, the API, benchmarks, etc. to help me plan future refinements.

Thanks,

-xdg

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.

Comment on Outside-in objects with Object::LocalVars
Select or Download Code
Re: Outside-in objects with Object::LocalVars (threads)
by tye (Cardinal) on Jul 28, 2005 at 14:15 UTC

    Drawbacks

    • Thread safety -- It has none. Can't be used with multiple threads.

    No?

    - tye        

      Hmmm. That's a big drawback. I'm really not very thread-savvy, so it's not something that was top of mind. Can you help me understand what about it makes it thread-unsafe? My limited understanding of threads was that all data is copied unless explicitly shared -- which I suppose, on reflection, does make me think that using the refaddr of the object as a key to the data is likely a problem when the blessed reference gets cloned into the new thread. (If that's true, I assume that problem affects inside-out objects, as well, then?) Maybe it makes sense to generate a unique ID for each object and store it in a blessed scalar used as the underlying object.

      What other things do I need to consider?

      Update: After some reading of perlthrtut, threads, threads::shared, Things you need to know before programming Perl ithreads, and Where Wizards Fear to Tread (perl.com), I'm not sure this is a "big" drawback. From what I can tell, the major problem is that the object ID gets lost across the creation of a new thread; that can be fixed by embedding a unique ID as described above. After spawning a thread, each thread is "safe" in that they won't interfere with each other, but it means that changes to object 1 in thread A don't appear in the corresponding object 1 in thread B -- but that's by design in the perl threads approach. To be able to share across the boundary should be theoretically possible by marking the package globals holding data as shared and implementing appropriate locking/semaphores. That complexity would appear to be present in any object framework that tries to synchronize an object across thread boundaries, not just this one. Still, I'll back-burner the idea of implementing a thread-safe version of this somewhere down the line -- at least the safe-across-a-spawn safe, and maybe beyond.

      Update2: In response to a post I made elsewhere, BrowserUk pointed out Re: Reliable asynchronous processing that addresses some FUD about threads in perl.

      -xdg

      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.

        "thread safety" is a general property, not something to do with a specific implementation of Perl threads.

        The implementation of Perl threads that you are commenting on is mostly like "forking, but without all of that efficiency", sort of the worst of both worlds (threading and forking). So my comment wasn't to do with how well you could make your stuff work with that implementation of threading.

        And having your object be usable with a threaded Perl so long as you don't share your object(s) between threads just means that that Perl's threading implementation isn't completely broken, not that your object is thread safe.

        Your method isn't thread safe because you can't have two threads using the same method from the same class at the same time on different objects -- where, again, I use the more common definition of "thread" here, that is, "two execution streams that share (almost) all of their virtual memory", not as in "two separate instances of Perl interpreters on related threads, one of which has laboriously and inefficiently made a near-complete copy of the other one".

        But you are correct in that lack of thread safety isn't a horrid problem here, because Perl has yet to have a decent threading model.

        BTW, when I released Data::Diver (just a few days ago), I used a "get last error reason" scheme despite the fact that it isn't thread safe. Normally my modules use objects, to avoid "global" problems such a thread safety and to support user configuration. This particular module didn't have any configurable bits to worry about and I felt using OO would have been much less convenient for most uses of the module. But that meant that my simple way of getting error details wasn't thread-safe. But I didn't want to complicate the important parts of the module to overcome this problem with less-important part of the module, especially since Perl doesn't really have good thread support anyway.

        But I did notice that I was doing something that wasn't thread-safe. It also isn't re-entrant, which becomes a problem if multiple layers of your application use it. I think your use of localized globals for access to member data is re-entrant in the absense of threads, though.

        So, upon reflection, given the current state of Perl, I guess I shouldn't have been surprised that you didn't mention nor worry too much about whether your Perl module was thread-safe (neither did I). Though I was also curious if my impression was correct.

        - tye        

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://478896]
Approved by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2014-09-24 04:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (245 votes), past polls