Skip to main content.

Tuesday, May 11, 2010

Last night we (Maik, Paul, Luca and me) had an interesting talk on "variable classes". Many scripting languages allow to change the structure of a class, and doing so, changing the runtime structure of the instances they have generated. Although this is generally regarded as "horror" in terms of object oriented programming, and relying on this is considered a bad practice, Maik and Paul (exp. Paul) convinced me that the thing has "a meaning" when profiling the an application at design/setup time.

Also, if specifically constrained and shaped down with the help of well-defined language extensions, as the type contracts we're designing, altering a class may not be as bad as it sounds.
The main reason why Falcon classes had never been flexible was because there was optimization opportunities in both the Falcon-language side and in the C embedding side which couldn't be caught otherwise, at the time when the first implementation was planned (but notice that METHODS are first class entities in Falcon, as functions, so it was possible to make normal properties methods after instance creation by just assigning a callable item to them). Later on, dictionaries and Tables was added to create classless instances and pervasive instance-control (table columns). So, it's not really flexibility that was leaking; if you want flexibility in your code, you could opt for POOP constructs in blessed dictionaries, array bindings and tables.

However, the ability to transform a type, and not just a prototype at runtime is something slighly different from what we have. And it has a ring, for how dangerous it may seem.

Another problem in changing a class structure is that falcon native instances store their property value in an array that must stay parallel with the class property dictionary. So, altering a class would disrupt the property-value relationship. Also, instance properties are undifferentiated, so explicitly declared class methods are just properties to which a method item is assigned by default at class creation.

And here is where the fun begins.

Falcon instance creation is extremely efficient, as it consists of just a flat copy of the class default property vector (and then, calling the constructor function, if needed). There's no tree-based dictionary copy involved; each instance is just a sequence of the Falcon Items it is supposed to hold.

This also allows for a thing that is quite interesting in the Falcon OOP model; while class structure is fixed, you have the ability to change the properties into methods seamlessly at runtime. But, how many users take advantage of this opportunity, and how much does it cost to us to keep it?

In the new model we're gonna introduce (the one I talked about in the previous blogs), we indicated the fact that we're moving the property control logic from the object instance to the class. This allows for an interesting change.

We can now put all the methods (those you declare with "function" keyword) under the control of the class, while the properties stays under the control of the objects.

Follow this example:

class MyClass
// a property
prop = "value"

// an instance-specific method -- a property where you store a function
imethod = function( param )
return param + self.prop

// a real method
function method( param )
return "method " + param + self.prop

inst = MyClass()

inst.prop = "new value" // obviously legal

inst.imethod = { param =>
return "changed "+ param + self.prop } // still legal

inst.method = { param =>
return "changed "+ param + self.prop } // now legal, but would become illegal.

MyClass.method = { param => reutrn "A new method!" } // now illegal, and this is the new part we want to have.

// Also, we want to do MyClass.addMethod and MyClass.removeMethod

Making illegal the second last statement we achieve two interesting results.
1) the vast majority of classes have few properties and many methods. Storing the methods in the owning class would further reduce the footprint of each instance, by a great deal.
2) classes could be changed at runtime; adding new methods would be immediately reflected on all the existing instances.
Adding new properties would be possible, but we may chose two ways of dealing with that:
2.1) ignore new properties on old instances, and apply them only on new instances.
2.2) mark the "generation" of the class and apply the changes to the instances in a lazy fashion, when they are accessed. As an old instance with a different generation is accessed, it is brought up to date inserting the newly added properties with their default value.

I'd lean for 2.1, because 2.2 would introduce extra ops at each access that are unneeded 99.9% of the time. But, 2.2 would be more consistent, eliminating an unnatural differentiation between the ability of alter the methods and the properties.

Of course, this ability would apply to CoreClasses and to HyperClasses (using the definitions we previously introduced); as UserClasses are actually instructions for Falcon to access user data, applying this on a UserClass may either
1) raise an exception or
2) create a HyperClass and a synthetic CoreClass, and substituting the UserClass with this new HyperClass.

I'd lean for 1, considering user-provided classes nearly "final"; also, in case of absolute need, a code like that:

class Changeable from AnUserClass

would take care of the problem.


No comments yet

Add Comment

This item is closed, it's not possible to add new comments to it or to vote on it