Jonathan raised a question in the #parrotsketch meeting today about our
strategy for safely changing classes that already have instantiated
objects. The core problem is this: when you create a class,
.local pmc classobj, object
classobj = newclass 'Foo'
instantiate an object from it,
object = classobj.'new'()
and then modify the class,
classobj.'add_method'('bar', $P3)
- At that point you want 'object' to still reference the old class, so
it can continue operating without disruption. No problem, you just don't
make any changes to it.
- And we can create a new class object inside the add_method call and
redispatch the add_method to it, leaving the original class object
unmodified.
- The problem is that after the call you want the 'classobj' variable to
contain the new class, rather than the old class. We don't have an
effective way of morphing that variable to the new class from within a
method call on that class. Or, more specifically, we do, but only by
altering the class itself, which will morph all references to the class,
including the references from the instantiated objects (which should
reference the old class instead).
Some alternatives:
1) Do away with safe morphing. Allow all dynamic changes to the class
even after it has been instantiated. Flag in the class that it's been
modified after instantiation, and require each object to check that flag
before it calls a method or accesses an attribute. When the class
changes, the object will have to reinitialize with the structure to
support the new class. Disadvantage: Lots of overhead on checking for
class modifications, even though modifying a class after it's
instantiated is actually pretty rare. (Not my favorite alternative.)
2) Modify the class object so it stores "version" information about
itself (such as an array of pointers to previous class objects). The
instantiated object would store both a pointer to its class and a
version number for the class. Disadvantage: This adds more complexity to
the core object model than seems merited by a feature that's rarely
used. (Not my favorite alternative.)
3) Modify the core PMC implementation so it tracks information about
which the different containers (registers, temporary variables,
namespace entries, etc.) that hold a particular PMC, and which
particular container was used to make the method call, so a method call
can modify the variable it was called on. Disadvantage: This isn't a
generally useful feature. (Not my favorite alternative.)
4) Do away with automatic morphing. Attempting to make changes to a
class that's already been instantiated is not allowed (add_method, etc.
will throw an exception). This also eliminates the need for
remove_method, remove_attribute, remove_role, and remove_parent. To
modify a class that has instantiated objects you must clone it first:
classobj = newclass 'Foo'
object = classobj.'new'()
newclassobj = classobj.'clone'('exclude_methods'=>$P1)
newclassobj.'add_method'('bar', $P3)
object2 = newclassobj.'new'()
And 'clone' could take an option specifying whether this particular
clone will replace the existing class in the namespace. Disadvantage: It
eliminates some of the DWIMery of the system. On the other hand, it
might make it easier to implement a language that doesn't allow
modifications to instantiated classes. (This is currently my favorite
alternative.)
Allison