Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Improve object clonning docs
- Document clone behaviour of %. and @. attributes
    (Closes #1458)
- Shorten wording
- Move custom clone examples to Mu.clone docs
- Mention private attrs get cloned too
- Remove "magical"; we don't have any magic
- Remove incorrect and somewhat rambly paragraph about
    Mu.clone being a submethod… it isn't
  • Loading branch information
zoffixznet committed Sep 1, 2017
1 parent 5ede23f commit fb2d3fc
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 42 deletions.
56 changes: 17 additions & 39 deletions doc/Language/objects.pod6
Expand Up @@ -524,47 +524,25 @@ say RectangleWithCachedArea.new( x2 => 5, x1 => 1, y2 => 1, y1 => 0).area;
=head2 Object Cloning
The C<Mu> parent class, from which all classes inherit, supplies a
method named L<clone>, which is somewhat magical in that it can copy
values from an object's private attributes to create a new object.
This cloning is shallow since it only binds attributes to
the same values contained in the original object; it does
not make copies of those values.
As with C<new>, public attributes can be set to initial values.
These override values received from the original object. (See
the documentation for Mu's L<clone> for an example.)
Note that since C<clone> is not a C<submethod>, a class which provides
its own C<clone> method will replace the C<Mu> method. There is no
automatic mechanism like C<BUILDALL> for cloning. For example, if one
wished to make clone deeper for a particular class, one would will
probably want to use C<callwith> or C<nextwith> to push the deeper
copies to superclasses:
class A {
has $.a;
#...
method clone {
nextwith(:a($.a.clone))
}
}
The cloning is done using L<clone> method available on all objects, which
shallow-clones both public and private attributes. New values for I<public>
attributes can be supplied as named arguments.
This works well for simple classes, but in some cases one might need
to follow C<BUILDALL>'s lead and work in reverse method resolution
order:
=begin code
class Foo {
has $.foo = 42;
has $.bar = 100;
}
=begin code :preamble<class A {};>
class B is A {
has $.b;
#...
method clone {
my $obj = callsame;
$obj.b = $!b.clone(:seed($obj.a.generate_seed));
$obj
}
}
=end code
my $o1 = Foo.new;
my $o2 = $o1.clone: :bar(5000);
say $o1; # Foo.new(foo => 42, bar => 100)
say $o2; # Foo.new(foo => 42, bar => 5000)
=end code
See L«documentation for C<Mu.clone>|/routine/clone» for details on how
non-scalar attributes get cloned, as well as examples of implementing your
own custom clone methods.
=head1 X<Roles|declarator,role>
Expand Down
57 changes: 54 additions & 3 deletions doc/Type/Mu.pod6
Expand Up @@ -162,9 +162,9 @@ Returns the object it is called on.
method clone(*%twiddles)
Creates a shallow clone of the invocant. Alternative values for I<public>
attributes can be provided via named arguments with names matching the
attributes' names.
Creates a shallow clone of the invocant, including shallow cloning of private
attributes. Alternative values for I<public> attributes can be provided via
named arguments with names matching the attributes' names.
=begin code
class Point2D {
Expand All @@ -180,6 +180,57 @@ say $p; # OUTPUT: «Point(2, 3)␤»
say $p.clone(y => -5); # OUTPUT: «Point(2, -5)␤»
=end code
Note that C<.clone> does not go the extra mile to shallow-copy C<@.> and C<%.>
sigiled attributes and, if modified, the modifications will still be available
in the original object:
=begin code
class Foo {
has $.foo is rw = 42;
has &.boo is rw = { say "Hi" };
has @.bar = <a b>;
has %.baz = <a b c d>;
}
my $o1 = Foo.new;
with my $o2 = $o1.clone {
.foo = 70;
.bar = <Z Y>;
.baz = <Z Y X W>;
.boo = { say "Bye" };
}
# Hash and Array attribute modifications in clone appear in original as well:
say $o1; # OUTPUT: «Foo.new(foo => 42, bar => ["Z", "Y"], baz => {:X("W"), :Z("Y")}, …␤»
say $o2; # OUTPUT: «Foo.new(foo => 70, bar => ["Z", "Y"], baz => {:X("W"), :Z("Y")}, …␤»
$o1.boo.(); # OUTPUT: «Hi␤»
$o2.boo.(); # OUTPUT: «Bye␤»
=end code
To clone those, you could implement your own C<.clone> that clones the
appropriate attributes and passes the new values to C<Mu.clone>, for example,
via L<nextwith>. Alternatively, your own C<.clone> could clone self first
(using C<self.Mu::clone> or L<callsame>) and then manipulate the clone as needed,
before returning it.
=begin code
class Bar {
has @.foo = <a b>;
has %.bar = <a b c d>;
method clone { nextwith :foo(@!foo.clone) :bar(%!bar.clone) }
}
my $o1 = Bar.new;
with my $o2 = $o1.clone {
.foo = <Z Y>;
.bar = <Z Y X W>;
}
# Hash and Array attribute modifications in clone do not affect original:
say $o1; # OUTPUT: «Bar.new(foo => ["a", "b"], bar => {:a("b"), :c("d")})␤»
say $o2; # OUTPUT: «Bar.new(foo => ["Z", "Y"], bar => {:X("W"), :Z("Y")})␤»
=end code
=head2 method new
multi method new(*%attrinit)
Expand Down

0 comments on commit fb2d3fc

Please sign in to comment.