Pitfalls with self-replicating objects
When designing a class, it's a good thing to consider reusability. The subject of this entry is to examine objects of a class that are able to replicate, and the consequences this has on making the class reusable.
Object replication means creating a new object from within the code. It doesn't have to be a copy, it just means here that a new object of the same class is created. There is no special consideration regarding the state of the new object.
Let's take a look at some sample code:
The effect of calling mitosis
is to create a new cell identical to the original.
Inheritance
All is good so far, however things become messy if we want to write a new class inheriting from BASIC_CELL. You noticed the create intruction, it calls make
and therefore make
must be available for creation for all descendants.
This can be a problem, for instance if the descendant has no obvious default state. It's also a problem if we don't want the outside world to create objects of the descendant class with make
. Fortunately, a work around this is to restrict the export state of make
, as the code below demonstrates.
This code fails to compile if void safety is turned on, and types are attached by default. In that case, there is no way for make
to find the correct value of cell_organ
.
Deferred descendants
Theoretically, a deferred class can inherit from a parent without trouble. But no deferred class can inherit from BASIC_CELL, because it would have to list make
as a creation procedure, and deferred classes cannot have them.
Fixing the code
A good workaround to this problem is to have a feature dedicated to the creation of the new object. The idea is that it can be undefined and redefined based on the descendants needs. Undefined in deferred classes, redefined to use the appropriate creation procedure otherwise.
For BASIC_CELL, the code becomes:
Here is an example of a deferred class with the undefine clause. Redefining the create_new
feature is left to next descendants in the hierarchy.
Conclusion
As we have seen, using create to duplicate an object makes a class harder to reuse, unless the use of create is restricted to be in a dedicated feature. Descendants can then undefine or redefine that feature in a way that suits their needs.
The compiler could detect this situation and issue a warning if the feature where create is found to contain other instructions.
Last but not least, perhaps someone could take a look at classes from the standard libraries, since at least TWO_WAY_LIST can replicate. I have myself written code that inherits from TWO_WAY_LIST, and working around this issue has annoyed me quite a bit, prompting this post.
The issue with deferred classes has been discussed at ECMA. At the time, we postponed any resolution on the solution because it was not felt as a major issue and it is easy to work around using the `create_new' mechanism you have described and the pitfall you mentioned.
I believe that if something is adopted it will be the following. When checking the code of a deferred class the creation of a
like Current' instance will always be valid if the call is valid. Clearly the non-deferred descendants of the deferred class will ensure that
make' is indeed properly exported for creation.