From: Robert Klemme Date: 2009-10-21T04:50:06+09:00 Subject: Re: deep cloning, how? On 10/19/2009 06:14 PM, Rajinder Yadav wrote: > On Mon, Oct 19, 2009 at 11:10 AM, Robert Klemme > wrote: >> On 10/19/2009 10:32 AM, Brian Candler wrote: >>> Robert Klemme wrote: >>>> 2009/10/16 lith : >>> Taking another example: I don't think you'll disagree that 99% of the time >>> you are expected to leave Object.new alone and instead define #initialize in >>> your own classes. But you wouldn't find that out from the documentation: >>> >>> $ ri Object.new >>> ------------------------------------------------------------ Object::new >>> Object::new() >>> ------------------------------------------------------------------------ >>> Not documented >>> >>> $ ri Object#initialize >>> Nothing known about Object#initialize >> Funny that you mention it: #new and #initialize on one side and #dup / >> #clone and #initialize_copy on the other side have one thing in common: >> object allocation is separated from initialization. I believe this was a >> wise decision because that way allocation policies can be implemented easier >> than in languages like C++ and Java where both are inseparable. > > I wonder as I mention already maybe this design has more to do with > the fact that Ruby does not perform static type checking like C++ / > Java does at compile time. In C++ you just declare a copy constructor > (initialize/constructor), if you have other (overloaded) constructor > code, then static type checking ensure the correct code logic is > executed, thus allowing you to write a cleaner clone method. C++'s copy constructor is not a "clone method". For example, it will happily "clone" any subclass instance. Cloning typically ensures at least the class of the new instance is the same as for the original. Static typing is just one reason why Ruby and C++ differ here: another important reason is the memory model of both languages. In Ruby you only have object references which can only be copied by value. In C++ on the other hand you have a whole toolbox of options (value objects, pointers, references - plus constant variants). You can see that when looking at Java: it has static typing but just one way to access objects - via references. This is the same model as in Ruby and alas, also Java has a method clone() which behaves similar (although the programming model is different), i.e. it creates a new instance of the same class with all members set to the same references as the original. Side note: I find Java's cloning is broken in several ways. If you want to make a class Cloneable you can only use "final" for primitive value members because otherwise you cannot prevent aliasing between old and new instance. Then, interface Cloneable does not contain method clone which does not make the compiler catch a missing public method clone(). Lastly, I would have preferred the return type to be generic; although I do have to admit that I did not think this through completely. I guess Sun's engineers had good reasons not to change this. > In Ruby > if initialize was called during cloning, you would need to add the > logic to perform the dynamic type checking test using Object.class. In other words: you would have to manually implement method overloading. > Who would want to write this boilerplate code over and over? So Ruby's > was around this was to use initialize_copy as I am going to assume > here. You make it sound like a workaround but it isn't. For a language like Ruby this is a good solution - and compared to Java's it's almost perfect. It just lacks the public recognition. :-) > I think cloning in the initializer code would be a better design if > Ruby did static type checking. The fact Ruby still does (dynamic) type > checking at runtime, means Ruby code gets penalized for performance. I don't follow you here. If you want a language with static type checking you'll have to look elsewhere. We don't have static type checking in Ruby - in fact it's one of the core assets of the language. Ruby with static typing would not be Ruby. Reasoning about which approach would be best if Ruby had static typing is pretty useless. > It seems the way Ruby does dup/clone/initialize/initialize_copy *throw > in subclassing* is a source of confusion for many and not really > intuitive, barring good or bad design. The length of this thread and > replies would seem to indicate this is a weakness in Ruby design, or I > am simply biased with my C++ background? Definitely better updated > documentation would help to ensure the correct policy to follow in > Ruby. I would attribute this confusion to the documentation and to the fact that this is a rare topic to come up. I cannot remember a "how to properly clone objects" thread in the last years that would have covered the topic as thoroughly as we did here. I don't think we are facing a weakness in Ruby's design here. C++ cannot be a role model for Ruby (regardless of whether you consider C++'s approach good or bad) because both languages are very different as I have tried to show above. It may be that your "C++ background" clouds your view on Ruby. :-) Thanks for the interesting discussion! Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/