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

Reply via email to