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

Does perl have any "object swizzling" capability - i.e. like Smalltalk's become: method?
my $x = bless {}, SomeClass; my $y = $x; say ref($y); # prints 'SomeClass'; my $z = bless {}, SomeOtherClass; ...do some magic so that $x 'becomes' $z... # note this also changes $y: say ref($y); # prints 'SomeOtherClass'

The question arose when I was thinking of implementations of lazy-loading. Have a proxy object stand in for the lazily-loaded object, and the proxy will load the real object only when it really is needed. Instead of having the proxy forward method calls to the real object, it would be nice to have the the proxy 'become' the real object.

Replies are listed 'Best First'.
Re: object swizzling?
by ELISHEVA (Prior) on May 05, 2009 at 19:12 UTC

    You can't turn the object referenced by $x into the object referenced by $z, except of course by assignment: $x=$z. You can however, rebless any reference into a new class:

    use strict; use warnings; sub Eng::speak { print "I speak English\n"; } sub Fr::speak { print "Je parle francais\n"; } my $x=bless({},'Eng'); print "<$x>\n"; #prints: <Eng=HASH(0x814ec28)> $x->speak(); #prints: I speak English bless($x, 'Fr'); print "<$x>\n"; #prints: <Fr=HASH(0x814ec28)> $x->speak(); #prints: Je parle francais

    As for proxy objects and lazy loading, there are numerous modules on CPAN for this purpose. See CPAN search: proxy object for starters.

    Best, beth

Re: object swizzling?
by moritz (Cardinal) on May 05, 2009 at 19:49 UTC
    Since what you call objects are really references, if you just re-bless $x into SomeOtherClass and assign it to $z, then both variables point to the same object, and methods that modify it will affect both variables.

    Of course if you want a proxy that just "intercepts" some method calls and forwards most of the rest, you could just inherit from the class you're trying to proxy.

      I guess that will work. I just have to make sure that my proxy is of the same underlying type (HASH, ARRAY, SCALAR) as the real object.
      Of course if you want a proxy that just "intercepts" some method calls and forwards most of the rest, you could just inherit from the class you're trying to proxy.

      In the lazy-load case I want to ensure that the real object is loaded before forwarding, so it's not that simple.

        In the lazy-load case I want to ensure that the real object is loaded before forwarding, so it's not that simple.
        You can call the super class's constructor first:
        package 'Bar'; use base 'Foo'; sub new { my $class = shift; my $parent_class_object = SUPER->new(); ... }
        And then store it, or use it, or rebless it, or whatever.
Re: object swizzling?
by ig (Vicar) on May 05, 2009 at 21:11 UTC

    I am not aware of any simple command and I rather doubt there is a completely general solution. What can be done depends on your definition of "becomes" and the details of the objects.

    Before your "magic" step, $x and $y both refer to one hash and $z refers to another hash.

    If you assign $z to $x then $x becomes $z in the sense that $x refers to the same object as $z, but $y will still refer to the original hash blessed into SomeClass.

    If you re-bless $x into SomeOtherClass then both $x and $y will both refer to a hash blessed into SomeOtherClass, but its content will not otherwise be modified.

    You could then copy the contents of the hash refered to by $z into the hash refered to by $x. This would make $x very similar to $z, but not the same. Depending on the internals of SomeOtherClass, $x and $z may not even refer to distinct instances - they may share data.

    You could perform a deep copy from the hash refered to by $z to the hash refered to by $x,but to be completely general you would need a deep copy that can cope with any complexity/magic that exists in the hash. For example, consider what might be necessary if the the hash has an elemnt that referes to a variable that is tied to external (file or database) persistent storage: how should such an element be copied? Consider also that class data should not be replicated. If the hash contains a reference to such class data then the deep copy would have to know not to replicate this. I can't imagine a general purpose algorithm that could deduce such aspects by inspection of the hash.

    If SomeOtherClass overloads assignment or has an appropriate copy method then you may achieve what you want by re-blessing $x into SomeOtherClass then assigning or copying $z to $x. In this case, presumably the object will "do the right thing".

Re: object swizzling?
by roubi (Hermit) on May 05, 2009 at 19:34 UTC
    Have you looked at turning your proxy into an alias (with one of the aliasing modules available on CPAN for example)? Never done it myself, simply throwing the idea out there.