Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Moose 'clone' Constructor

by kcott (Archbishop)
on Jul 04, 2018 at 07:47 UTC ( [id://1217859]=perlquestion: print w/replies, xml ) Need Help??

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

I seem to recall seeing somewhere a clone() method for Moose objects. I've looked in Moose and Moose::Manual::Construction but don't see anything like this. I've also had a poke around in Super Search but have found nothing promising.

In my current code, I writing this type of thing:

my $cloned_object = ref($existing_object)->new();

This works fine for what I'm working on at the moment because the classes in question have no required attributes and taking the defaults is exactly what I want in this instance.

I don't have any requirement to do anything different at the moment; however, it occurs to me that I might do in the future, perhaps something like:

my $cloned_object = ref($object)->new(%objects_attrs_and_values);

which might be better as something like:

my $cloned_object = $existing_object->clone();

So, is there such a thing as clone() or did I just imagine it? If it exists, please point me to documentation; if not, any related thoughts on this are welcome.

— Ken

Replies are listed 'Best First'.
Re: Moose 'clone' Constructor
by Arunbear (Prior) on Jul 04, 2018 at 09:54 UTC

      G'day Arunbear,

      ++ What can I say: great minds link alike. :-)

      When I got home from work, Corion's reply was the only response at that time. I followed the link he'd provided, followed some more links from that, then hit "reply". As I was composing, I noticed I now had a reply from you and thought: "Oh good, more information, I'll look at that in a minute." — and when I did, I then thought: "Hmmm, that looks familiar."

      Anyway, I've only just found it, perhaps you've used it. I noticed there's an additional argument:

      $metaclass->clone_object($instance, %params)

      Is %params as straightforward as it seems? I'm thinking maybe issues with values that are references to data structures, as opposed to simpler (Str, Int, etc.) values.

      — Ken

        %params are the same kind of data as were passed to the constructor when $existing_object was created (but now you have a chance to override those if needed).
Re: Moose 'clone' Constructor
by choroba (Cardinal) on Jul 04, 2018 at 12:49 UTC
    Note that there can't be a general cloning routine free to use in all the cases. Imagine each object has a temp file created somewhere - when cloning an object, do you want it to share the same file(handle)? What if each object registers itself to a static hash or a parent object, generates a unique ID, seeds a PRNG, etc?
    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; { package MyObj; use Moose; has fh => (is => 'ro', builder => '_build_fh'); sub _build_fh { my ($self) = @_; open my $fh, '>', "/tmp/$self" or die $!; return $fh } sub DEMOLISH { my ($self) = @_; close $self->fh; unlink "/tmp/$self"; } } my $o = 'MyObj'->new; { my $c = $o->meta->clone_object($o); } say { $o->fh } "Hello world!"; # say() on closed filehandle $fh

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

      G'day choroba,

      ++ That's a very valid point; thanks for raising it. It even applies to the code I'm currently working with, which I paraphrased for this post as:

      my $cloned_object = ref($existing_object)->new();

      As I indicated in the OP, I want an object that's of the same class as $existing_object, but I want default values (not whatever values $existing_object has). In fact, if I had taken $existing_object's values, that would've broken my code in all sorts of interesting ways.

      — Ken

Re: Moose 'clone' Constructor
by Corion (Patriarch) on Jul 04, 2018 at 07:56 UTC

    I see MooseX::Clone, which implements more cloning support, but alludes to some bare-bones cloning support in Moose. If I read the discussion right, cloning in Moose is just:

    bless { %$old }, ref $old;

    ... but I don't know if there is a default constructor ->clone() provided.

      G'day Corion,

      ++ Thanks for that. MooseX::Clone looks interesting. Its abstract does say "Fine-grained cloning support", and it looks like there could be a fair bit of work setting this up. Perhaps useful in situations where specific custom cloning was wanted. I also note a TODO section: I tend to be a bit wary of those as it suggests incomplete functionality — might wait for that to become TODONE. :-)

      With a bit of link-hopping from there, I came across "Class::MOP::Class - Object instance construction and cloning". The clone_object() method appears less fine-grained (than MooseX::Clone) and perhaps more useful as a general solution.

      Both of those have example code for setting up a clone() method in your own class.

      As I said in the OP, not an immediate requirement but I'll keep both of those in mind. Thanks again.

      — Ken

Re: Moose 'clone' Constructor
by tobyink (Canon) on Jul 04, 2018 at 16:32 UTC

    Others have already pointed out how you can use the metaobject protocol to clone objects with Moose. If you know that the object in question is a Moose object, this is a reasonable solution. I want to mention my module Object::Util though. It contains a bunch of utility methods which can be used with Moose objects and other objects too.

    use Object::Util; my $cloned_object = $existing_object->$_clone(%extra_parameters);

    Object::Util will first check if the existing object has a clone method, and if so, will just call that. After that, it falls back to using the metaobject protocol if it detects the object is Moose- or Mouse-based. Finally as a last resort, if the object is a blessed hashref, it simply does a shallow copy of the hashref and blesses the new hashref.

    Object::Util has a bunch of other cool things too, like:

    $cloned_object->$_extend({ my_new_method => sub { my $self = shift; print "yay!\n"; }, }); $cloned_object->my_new_method(); # says "yay!"

    Note that the methods provided by Object::Util are coderefs in scalar variables, so you call $object->$_method() instead of $object->method(). This is a way to make the methods available for all objects of all classes without polluting UNIVERSAL.

      G'day tobyink,

      ++ Thanks for pointing me to that module: I hadn't encountered it previously.

      I found the way you've used coderefs interesting. I suspect I may have seen something similar in the past but probably wasn't paying close enough attention. I'm certainly familiar with:

      $ perl -E 'my $x = sub { say "@_" }; $x->("fred")' fred

      And, as expected, this didn't work:

      $ perl -E 'my $x = sub { say "@_" }; $x("fred")' syntax error at -e line 1, near "$x(" Execution of -e aborted due to compilation errors.

      However, I've never written code like:

      $ perl -E 'my $x = sub { say "@_" }; "XYZ"->$x("fred")' XYZ fred

      I'll have to look into that further. I've read the "Object::Util - Rationale" section. Can you point me to any documentation that explains this in more detail?

      — Ken

        And, as expected, this didn't work:

        but this does

        perl -E 'my $x = sub { say "@_" }; &$x("fred")'

        as to "XYZ"->$x("fred"), I would not have expected it to work, becuase "XYZ" is not blessed to anything, but seeing that it does, it behaves like i expected, with $thing->method($arg) calling &method as method($thing,$arg)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2024-04-24 23:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found