"Paul Brannan" <pbrannan / atdesk.com> schrieb im Newsbeitrag
news:20040615171413.GY2788 / atdesk.com...
> On Tue, Jun 15, 2004 at 12:08:37AM +0900, Robert Klemme wrote:
> > You need to return 'copy' here.
>
> Good catch, thanks.
>
> > > 3. Should dup be implemented in terms of clone or should clone be
> > >    implented in terms of dup (or should they both be implemented
> > >    independently or in terms of another function)?
> >
> > IMHO neither since #dup and #clone do have different semantics with
> > respect to freeze.  Well, you could do something like this:
> >
> > class Foo
> >   attr_accessor :foo, :bar
> >
> >   def dup; do_copy( super, :dup ); end
> >   def clone; do_copy( super, :clone ); end
> >
> >   protected
> >   def do_copy(copy, sym)
> >     copy.instance_eval do
> >       @foo = @foo.send sym
> >       @bar = @bar.send sym
> >     end
> >
> >     copy
> >   end
> > end
>
> I like this, since it works on both 1.6 and 1.8.
>
> > but this does not work for #clone if the instance is frozen.
>
> Right, because the copy has already been frozen when we start to modify
> it.  I wonder if there's a good way around that (without using
> initialize_copy, since that doesn't work on 1.6).  Maybe I should
> re-implement clone/dup in ruby (as you've done below) for Object so that
> it has the same behavior as 1.8.

Well, you could reimplement #clone in terms of #dup and apply all changes
(freeze and taint) aftewards.  You'll be quite likely to end up with a
similar scheme to #initialize_copy, so the question is whether it's
worthwile.

> > This might be better:
> >
>
> It's much better, though it seems a bit heavyweight.
>
> > class Foo
> >   attr_accessor :foo, :bar
> >
> >   def clone; copy :clone; end
> >   def dup; copy :dup; end
> >
> >   protected
> >   def copy(sym)
> >     c = self.class.new
> >
> >     copy_init c
> >
> >     c.freeze? if frozen
> >     c.taint if tainted?
>
> I think this should read:
>
>       if sym == :clone then
>         c.freeze if frozen?
>         c.taint if tainted?
>       end

Yes, of course!  Thank you!

> >     c
> >   end
> >
> >   def copy_init(c)
> >     c.instance_eval do
> >       @foo = @foo.sent sym
> >       @bar = @bar.sent sym
> >     end
> >   end
> > end
> >
> > class Bar < Foo
> >   attr_accessor :name
> >
> >   protected
> >   def copy_init(c)
> >     super
> >     c.instance_eval do
> >       @name = @name.send sym
> >     end
> >   end
> > end
> >
> >
> > > 4. When making the copy, is super the right way to make the copy, or
> > >    should allocate be used? (allocate doesn't work on 1.6.x, which I
> > >    still use heavily, though if it's the right solution, then it's
the
> > >    right solution).
> >
> > I'm inclined to follow the Java clone() pattern and use super.  Of
course
> > you can also just do self.class.new.
>
> The problem with self.class.new is that the initialize method may have
> some semantics that are undesirable when copying.

Exactly.

> > > 5. Certain types cannot be dup'd (e.g. NilClass in all versions of
Ruby
> > >    or Fixnum in 1.8 and later), so the above code will break if I
write:
> > >
> > >      Foo.new(10, 42).dup
> > >
> > >    Is it possible (or is it even wise) to write a dup or clone
function
> > >    that works with both value types and container types?
> >
> > It depends on whether you expect these types as members and what you
want
> > to be done with them.  It's difficult to anwer this generally, that's
why
> > the automatic methods just do a shallow copy.
> >
> > Generally speaking IMHO NilClass#dup should return self, same for
Fixnums
> > and others.  But OTOH I can see why Matz did it this way: so you get
to
> > know that some instance is not cloneable.
>
> That's exactly what I was thinking, though I was hoping for a better
> answer.

:-)

Maybe Matz has some other reason.

Kind regards

    robert