Tuesday, November 20, 2007

Stating the obvious

Should we always state the obvious? Interface types can only declare methods, properties and events (and assertions, since we're talking about Freya!). Fields, in any case, are not allowed inside an interface declaration.
Properties, however, are designed most of the times to resemble fields. Why should we stick to the property keyword when declaring properties for an interface type?
IScene = interface
Sampler: ISampler; readonly;
Camera: ICamera; readonly;
Root: IShape; readonly;
Lights: Array[ILight]; readonly;

method Render: PixelMap;
method Render(Row, Col: Integer): Pixel;
end
Of course, if you feel uneasy about dropping the keyword, you can still use it. Notice that there's no possible confusion, even with the declaration of Lights: that's a property returning an array, not an array property.
By the way, the above code fragment shows another omission: that of the final semicolon after the type declaration. Freya syntax has been relaxed to use optional semicolons in several places: after the end keyword in a complex type declaration and after the statement block in a method implementation. We're not going as far as Eiffel, where semicolons are optional almost everywhere, but our new syntax makes easier manual code translation from C# into Freya.

Labels: , , ,

Wednesday, August 22, 2007

Elisions

Less typing means faster programming, and simple syntactic changes can save a lot of keystrokes. The newest Freya syntax allows you to drop the do and then keywords when they precede a statement starting with another keyword.
In this fragment, both the for and the while statements have dropped their do's:
method GetPrimes(Max: Integer): BitsArray;
begin
Result := new BitsArray(2, Max);
for i in 2..Max div 2 : not Result[i]
begin
var
j := i + i;
while j <= Max
begin
Result[j] := true;
j += i;
end;
end;
Result.Invert;
end;
Note that we have also dropped the for var combination that used to mark local variable type inference in previous versions.
There is another interesting detail in the above code:
    for i in 2..Max div 2 : not Result[i]
First, there's a seemingly innocent variation of the classical for/to numerical loop: we have disguised it as if we were iterating over a numerical range. We still cannot eliminate the classical statement, since this "iterator" does not substitute the downto loop.
However, the real purpose of this change has to do with the Boolean expression after the semicolon. It acts as a filter, and it can be used both with virtual numerical ranges and with real iterators.
At the end, an iterating filter translate as a nested if statement... but you have saved some typing, and your code is a little more expressive, since you don't have to look for a matching else before understanding the purpose of that nested if.

Labels: ,

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]
private
Items: Array[X];
Count: Integer;
public
constructor(Capacity: Integer);
begin
Items := new Array[X](Capacity);
end;

constructor : Self(128);

// ...
end;
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)
public
constructor
(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);
end;
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));
constructor
: 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:
public
static operator+(c1, c2: Complex): Complex;
begin
Result := new Complex(
c1.Re + c2.Re, c1.Im + c2.Im);
end;

static operator-(c1, c2: Complex): Complex;
begin
Result := new Complex(
c1.Re - c2.Re, c1.Im - c2.Im);
end;
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:
public
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:
public
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
public

Origin, Direction: Vector;

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

Labels: ,