Sunday, July 08, 2007

Be constructive

We have already seen how useful can be expression based method implementations. Is there anything similar for constructors? Constructors have no return value, so they cannot directly benefit from expression based implementations... but it turns out there's a surprisingly similar feature at our reach.
In a sense, a constructor can be viewed as a static function (a method returning some value). The returned value is, of course, a new instance from the class the constructor belongs. In any case, there are a couple of constraints for constructors that regular methods have not: the return type must always be the same, inside a given class, and the returned instance must always be a new instance.
Note: It could make sense to remove the latter restriction. It's an important part of the contract for languages with explicit memory deallocation. Now think how you must implement an instance cache for a given class: since you cannot overload the new operator as in classic C++, you must search all creation expressions along the whole application to substitute them with method calls.
Given the previous constraints, and the fact that constructor calls are functional calls, we can find the equivalent to expression-based implementations: chained calls to sibling or parent constructors... a feature that was there all the time:
Stack = class[X]
Items: Array[X];
Count: Integer;
constructor(Capacity: Integer);
Items := new Array[X](Capacity);

constructor : Self(128);

// ...
It's true it looks so different from regular expression-based implementations, but it's a historical consequence: chaining has been available in C++ from long time ago. The only novelty is that you can omit the whole method block from that point on.
It's recommendable to write always this kind of "bodiless" chaining in the class declaration, instead of using an implementation section. Neither C# nor Freya supports default parameter values, so it's very frequent to find a handful of constructors in a given type, each of them removing some parameters from the signature's tail. We can use chaining to implement all of them, except the first:
Metal = sealed class(IMaterial)
(Color: Pixel;
MinRefl, MaxRefl, Diffuse: Double;
PhongAmount, PhongSize: Double);

constructor(Color: Pixel;
MinRefl, MaxRefl, Diffuse: Double) :
Self(Color, MinRefl, MaxRefl, Diffuse, 0, 1);
constructor(Color: Pixel;
MinRefl, MaxRefl: Double) :
Self(Color, MinRefl, MaxRefl, 1, 0, 1);
constructor(Color: Pixel; MinRefl: Double) :
Self(Color, MinRefl, 1, 1, 0, 1);
constructor(Color: Pixel) :
Self(Color, 0.1, 1, 1, 0, 1);
There's a second related question: is there any similar feature for iterators? I have an affirmative answer, but the syntactic details still need some boiling...

Though it's not an official Freya feature, it could also make sense to bring back another old C++ feature: field initialization in constructors. We could have written the previously shown Stack constructors like this:
// This is not Freya... yet...
constructor(Capacity: Integer)
: Items(new Array[X](Capacity));
: Self(128);
Of course, this enhances the expressivity of chaining, but that's not the main point. The main reason to bring this feature back has to do with programming by contract: this kind of field initialization may be necessary for implementing not nullable reference types, as in Spec#.

Labels: , , ,


Post a Comment

<< Home