Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Automatic vivification of an object

by bounsy (Acolyte)
on Jan 14, 2015 at 03:14 UTC ( [id://1113173]=perlquestion: print w/replies, xml ) Need Help??

bounsy has asked for the wisdom of the Perl Monks concerning the following question:

I'm interested in some thoughts and comments on some code I recently developed to allow for automatic vivification of objects. If this looks reasonable, I would like to submit a module to CPAN.

The basic idea is that instead of creating an instance of a class, you would create an instance of the autovivifying class that calls an initialization function the first time it needs to and from then on you would have an object of the desired class.

Here's the code:

#!/usr/bin/perl use strict; use warnings; package AutoVivify; use vars qw/$AUTOLOAD/; sub new($$) { my ($class, $initializer) = @_; die "Initializer must be a code reference." unless (ref($initi +alizer) eq 'CODE'); my $self = {initializer => $initializer}; bless($self, $class); return $self; } sub AUTOLOAD { $_[0] = &{$_[0]->{initializer}}(); $AUTOLOAD =~ s/.*:://; eval "\$_[0]->$AUTOLOAD(\@_)"; } sub DESTROY { } 1;

Here's an example of usage:

#!/usr/bin/perl use strict; use warnings; package foo::bar; sub new() { return bless({}, shift); } sub aaa() { my $self = shift; warn "aaa"; $self->{foo} = "bar"; } sub bbb($$) { my $self = shift; warn shift; warn $self->{foo}; } package main; use AutoVivify; my $x = AutoVivify->new(sub {return foo::bar->new()}); $x->aaa(); #$x is now an object of type foo::bar $x->bbb(2);

One possible usage for something like this is for classes that have expensive initialization for an object. This defers initialization until absolutely needed without requiring the class to support deferred initialization.

Any suggestions for module names are appreciated as well (AutoVivify, Class::AutoVivify, etc.).

Replies are listed 'Best First'.
Re: Automatic vivification of an object
by BrowserUk (Patriarch) on Jan 14, 2015 at 03:41 UTC
    The basic idea is that instead of creating an instance of a class, you would create an instance of the autovivifying class that calls an initialization function the first time it needs to

    This smakes of a solution looking for a problem.

    Two questions:

    1. Why would I instantiate an instance of a class that I'm not going to use?

      Smacks of a bad cure for a bad scoping problem; better corrected by correctly scoping the instantiation.

    2. In what scenario is there a benefit in deferring the instanciation from where I declare the instance, to the point of first use?

      Quantify the benefit.

    A third question: Why does your "example of usage" not actually run?

    C:\test>t-autoVivify.pl Can't locate object method "new" via package "autovivify" (perhaps you + forgot to load "autovivify"?) at C:\test\t-autoVivify.pl line 29.

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
      1. The reason I came up with this was actually because I was looking for a solution to a specific code problem that I have. I have a class that is part of an inheritance tree of classes (in the middle) that needs to have an instance of that class as a shared variable (declared using "our"). It also needs to have access to variables declared in other classes in the tree. Because of the way the inheritance is working out, I'm having difficulty finding a way to handle this without delayed initialization. And, instead of coming up with a class-specific solution, I was trying to come up with a generic solution.
      2. I think I already answered this in #1, above.
      3. It was extracted from the code I had been using to play around with the concepts. I've modified the example usage and it should work now. (I added the "use AutoVivify;" line and corrected the case on the line where a new instance is being created.)
        I have a class that is part of an inheritance tree of classes (in the middle) that needs to have an instance of that class as a shared variable (declared using "our"). It also needs to have access to variables declared in other classes in the tree. Because of the way the inheritance is working out, I'm having difficulty finding a way to handle this without delayed initialization.

        Then you are fixing the symptoms of the problem rather than the problem itself.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
        Although I don't want to discourage the development of a solution if it works, is tested, and is useful, I tend to agree that it sounds like your problem might have a more direct solution, by updating the OO design. If a parent class needs access to a subclass's property, an abstract accessor is a solution, and if a subclass needs access to a parent, then an accessor should do. As for a class offering an instance of itself, maybe a short example would help to see what you're doing. Maybe there is some other thing that could be delayed from initialization time to runtime whose solution would come more naturally.
Re: Automatic vivification of an object
by LanX (Saint) on Jan 14, 2015 at 05:31 UTC
    As a side note, prototypes have no effect on methods.

    Update

    And that eval

     eval "\$_[0]->$AUTOLOAD(\@_)";

    is unneeded and very inefficient.

    Cheers Rolf

    PS: Je suis Charlie!

      Thanks for the tip about eval. I didn't know Perl allows method names to be called via string variables. I thought an eval was needed. So my updated version of that eval line is:

      $_[0]->$AUTOLOAD(@_);

      As for prototypes, I tend to add them to method declarations as a form of minimal documentation to help me quickly understand what data types the method is expecting when I'm debugging code. (I also add more extensive POD and # comments, but the prototypes give me a quick snapshot.)

        > As for prototypes, I tend to add them to method declarations as a form of minimal documentation to help me quickly understand what data types the method is expecting

        Sorry but that's dangerous nonsense.

        Using anything else than $ would be misleading hence even $ is redundant.

        And even $ will make Class->method() act different to Class::method()

        Cheers Rolf

        PS: Je suis Charlie!

        To elaborate on LanX’s comments:

        Before using Perl prototypes you should study Tom Christiansen’s essay, “Far More than Everything You’ve Ever Wanted to Know about Prototypes in Perl, ” which is available within the Monastery here. To quote from the opening:

        Nearly any programmer you encounter will, when asked what function prototypes are for, report the standard text-book answer that function prototypes are mainly used to catch usage errors at compile time in functions called with an unexpected type or number of parameters. This is what programmers are expecting of prototypes, and what Perl does not give them.

        Think coercion rather than checking, and you’ll be closer to the mark. Read the article, then use prototypes only for those specialised cases where they’re really useful.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: Automatic vivification of an object
by Your Mother (Archbishop) on Jan 14, 2015 at 03:48 UTC

    I suggesting reviewing this thread too: Object method prototypes?. Moo/Moose/Mouse all let you defer every aspect of attribute instantiation already and Mouse (and maybe Moo) is likely going to be faster than anything using $AUTOLOAD.

      I would love to be able to use Moose or something like it, but that's not available to me at my current workplace and is unlikely to be in use any time soon.

      The use of AUTLOAD in this case should involve minimal impact since it only ever calls it once (at most). From then on, it is an object of the correct type.

      I'm going to have to take a closer look at Moo. It looks like I might be able to use that in the future, since it appears to be pure Perl. (The XS code is what makes Moose unusable currently due to environmental issues that include too many versions of Perl across multiple operating systems.) Unfortunately, Moo's not going to be able to help my current project.

Re: Automatic vivification of an object
by tobyink (Canon) on Jan 14, 2015 at 12:06 UTC

      That looks like it would work for me... if it was available. :-(

      The problem is that I need either use a module that is already available (mostly core modules plus a few other standard modules) or I need to use a pure Perl implementation due to environmental constraints (which include the lack of any compilers on any servers other than the servers used for building new software). From the documentation, it looks like Data::Thunk uses XS.

Re: Automatic vivification of an object
by sundialsvc4 (Abbot) on Jan 14, 2015 at 16:35 UTC

    And if what you want is “lazy initialization” of the instance, you can define a subroutine like this one and be sure to call it in every other method:

    sub _init { my $self = shift; unless ($self->{'_initialized'}) { // Do some other stuff here maybe calling ::SUPER methods $self->{'_initialized'} = 1; // do this NOW not sooner } return $self; }

    Since this method returns $self, it can be easily used “in-line” to make it unobtrusive and easy to call, for example:

    sub whatever_it_is { my $self = shift -> _init; ...

    As you can see, the initialization actually only takes place the first time that the function is called.   (It is significant that the flag-variable is set to True at the end of the unless block, e.g. if you are callng superclass routines and need to make sure that they do not perceive that the object is already lazy-initialized.)   (Also, I am using the common nomenclature of prefixing “internal, don’t pay attention to me” identifiers with an underscore.)

    Yes, it does put a burden upon you to be sure to call the routine at the start of every other method, other than new, as illustrated, but it is unobtrusive and it works.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1113173]
Approved by NetWallah
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (8)
As of 2024-04-23 13:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found