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: , , ,

Saturday, July 07, 2007

Notepad Oriented Programming

One of the shameless goals of Freya is to become a Notepad Oriented Programming Language: you must be able to write Freya applications with the Notepad and little more (Reflector, perhaps?). But that's a pretty hard goal when you're designing a language inspired by the Algol/Pascal lineage. It's not only that you must use those begin/end blocks instead of curly braces. If you must keep method declarations apart from their implementations, as in Delphi, then you'll have to type a lot... or lean on an editor that replicates the missing implementations by user request.
I think we have achieved the above stated goal. Right now, you can write Freya code that looks as compact and easy as C# code, and in some circumstances, the Freya variant may be even shorter than the C# equivalent. To illustrate this, let's take a look at C# and Freya operators. Let's say we're writing a Complex class in C#. Here you have two user defined operators on that class:
// C#
public static Complex operator+(Complex c1, Complex c2)
return new Complex(c1.Re + c2.Re, c1.Im + c2.Im);

public static Complex operator-(Complex c1, Complex c2)
return new Complex(c1.Re - c2.Re, c1.Im - c2.Im);
In a first attempt, those two operators would be translated to Freya this way:
static operator+(c1, c2: Complex): Complex;
Result := new Complex(
c1.Re + c2.Re, c1.Im + c2.Im);

static operator-(c1, c2: Complex): Complex;
Result := new Complex(
c1.Re - c2.Re, c1.Im - c2.Im);
There's nothing to be especially proud of in the above example: our fragment is longer in Freya than in C#. It's true that we have spared ourselves from some Delphi.NET eccentricities. For instance, we have written inline implementations, avoiding those nasty duplications imposed by the interface/implementation artificial split. We have also saved something in constructor calls: we use new as in C#, instead of calling some named constructor, as Delphi requires. It has nothing to do with saving two or three characters in each call (we're loosing that tiny advantage by using static instead of class, as in Delphi), but our syntax makes easier to translate existing code from C# to Freya. Last, but no least, we can use symbolic names for the operators, instead of Add and Subtract, as in Delphi or Chrome.
Expression based implementations will let us simplify the above listing:
operator+(c1, c2: Complex): Complex =>
new Complex(c1.Re + c2.Re, c1.Im + c2.Im);

operator-(c1, c2: Complex): Complex =>
new Complex(c1.Re - c2.Re, c1.Im - c2.Im);
We have deleted both begin/end blocks, and both assignations to Result. Since operators are always public and static in .NET, we have also dropped the static modifier. We now have code comparable to C# in length, and maybe even shorter. But we can keep shortening our example:
operator+(c1, c2: Complex) => new Complex(
c1.Re + c2.Re, c1.Im + c2.Im);

operator-(c1, c2: Complex) => new Complex(
c1.Re - c2.Re, c1.Im - c2.Im);
We are showing the last addition to Freya: return type inference for expression-based implementations. This is not a full featured type inference, as in functional languages or a modern language as Nemerle. The Freya compiler only allows the omission of the return type when it finds an expression based implementation, and when the implementing expression is an instantiation expression. We think that complex inferences are not a good thing, at least with a language as Freya, so we have added some inference... up to a sensible point.
We can use return type inference in yet another case, as this example shows:
operator/(c1, c2: Complex) =>
using r2 := c2.Re.Sqr + c2.Im.Sqr do
new Complex(
(+c1.Re * c2.Re + c1.Im * c2.Im) / r2,
(-c1.Re * c2.Im - c1.Im * c2.Re) / r2);
In this case, we have a common expression block containing a new expression, so we can safely deduce the return type before resolving the whole expression.
Of course, you won't be forced to write code in this style: if you want your code to look like good old Pascal, you still can write it that way... and I'm not being sarcastic. There's an important problem with compact code: how you get there. When you write programs by assembling small pieces into bigger ones, the result will contain extra code and glue that you probably won't need. The most frequent reason has to do with the fact that modular code, as you store it in your mental pattern library, must deal with a yet unknown context, so it probably has extra checks for handling extreme cases and such. When you adapt those code pieces for a given task, some special cases render improbable, and the corresponding guarding code can be deleted.
A second source of redundant coding is gluing. How do you compute the square root of the Zipperstein-Marmaduke Formula? First, you must evaluate the ZMF and then you'll have to find that pesky square root. In your first attempt, it's highly probable that the ZMF return value was stored in a local variable. There are two possibilities about the final code: either you can keep a separate line for dealing with ZMF, just to keeping what your code does, or you can merge both computations in a single expression. It's up to you to decide.
The consequence: compact code may be easier to read than to write... as the failure of functional programming languages to gain enough users has shown. Freya doesn't require you to write the shortest possible code, but that's still an option you have once you master the language.

... by the way, now we can write the Ray class as follows:
Ray = sealed class

Origin, Direction: Vector;

property Items[Time: Double] => new Vector(
Origin.X + Time * Direction.X,
Origin.Y + Time * Direction.Y,
Origin.Z + Time * Direction.Z);

Labels: ,