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.

> 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

> 
>     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.

> > 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.

Paul