Wolfgang De Meuter
Last revision: August 7th, 1996

Cloning Methods


Agora cloning methods (see below) are useful for avoiding the so called prototype corruption problem most prototype-based languages suffer from. Consider the following program fragment taken from Gunther Blashek.

welcomeMsg :=String;
welcomeMsg add:"Hello Fred!";

...

goodByeMsg := String copy; goodByeMsg add:"Good Bye,Fred!";

A subtle error that may be difficult to trace has been made here. By sending the add: message to the welcomeMsg alias of the String prototype, the prototype has become destructively changed. In a completely different part of the program, the String prototype might be cloned as in the last two lines in the program fragment. Although the programmer would expect the goodByeMsg to be an empty string object, freshly created from cloning the String prototype, the goodByeMsg will unexpectedly contain "Hello Fred!Good Bye, Fred!" as value. The problem is that a prototype is cloned after it was destructively changed through an alias of the prototype. This problem is called the prototype corruption problem.

Cloning Methods

The prototype corruption problem can be tackled from two different corners. Either we restrict the access priorities of aliases. This is a difficult task and needs extensive data flow techniques. For example, right after the first two lines in the code fragment, we could decide to assign welcomeMsg back to String, making it very difficult for a system to keep track of 'real' prototypes and aliases of prototypes.

Another route to a solution is to control the way objects are cloned. Of course, this is exactly what cloning methods do. Cloning methods allow a programmer to protect its prototypes in several layers. For example, we might decide to define the add: method as a cloning method. This way, many aliases of the String prototype can be created, but if one tries to touch an alias as in the case of the welcomeMsg, a clone is returned. Hence, the strongest level of safety a prototype can require is achieved by declaring all its destructive methods as cloning methods. A more relaxed way of ensuring safety in the above code fragment is to allow the String prototype to decide the way in which it will be cloned. In the hypothetical language used by the example, the String prototype is cloned 'from the outside' by the copy 'operator'. The String prototype has no control over what is happening. This can be avoided if the copy 'operator' is replaced by a cloning method.

Briefly spoken, cloning methods are methods that will be executed in a copy of the receiver. Their result is the clone of the receiver in which they were executed. The following example illustrates the most simple use of cloning methods.

[ 
complex variable: [ real variable: 0;
                     imag variable: 0;
                     new  public cloning method: []
                     + c public imperative method:
                      [ real: (real + c real);
                        imag: (imag + c imag) ] ];

complex real:17;
complex imag:28;
complex2 variable: complex new;
complex2 real: 27;
complex2 imag: 18;
complex + complex2
]
The cloning method 'new' has an empty body. Hence, upon invocation of this cloning method, the body will be executed in a copy of the receiver (doing nothing since the method contains no code) and finally, that copy will be returned. Hence, the new cloning method of the example simply returns a copy of the receiving object. Notice that clone methods are executed in a shallow clone of the receiving object. So, in the following example,
[ 
SIPCP variable:
  [ title  variable: "Structure And Interpretation";
    author variable: "Abelsson and Sussman";
    new public cloning method:[]
  ];

course variable:
  [ book variable: SIPCP;
    student variable: 
      [ name variable: "Eva Luator";
        address variable: "Brussels";
        new public cloning method: []
      ];
    new public cloning method: []
  ];

course2 variable: course new
]
course2 will be a shallow clone of course. The contents of course (i.e. book and student) will not be copied by the unary cloning method new of course. The following adapted version of the example performs a deep copy of the course object.
[ 
SIPCP variable:
  [ title  variable: "Structure And Interpretation";
    author variable: "Abelsson and Sussman";
    new public cloning method:[]
  ];

course variable:
  [ book variable: SIPCP;
    student variable: 
      [ name variable: "Eva Luator";
        address variable: "Brussels";
        new public cloning method: []
      ];
    new public cloning method: [ book new;
                                     student new
                                   ]
  ];

course2 variable: course new
]
It is important to understand the new cloning method of the course object. Since it is a cloning method, it is executed in a copy of the receiver. Hence the book: and student: messages will set the book and student variables in the clone. Since cloning methods perform a shallow copy of the receiver, the book and student variables point to the originals upon entering the cloning method. Hence, book new and student new will be sent to the right objects.