Saturday, May 14, 2005

On properties and events

The simplest way to declare an event in C#, only requires from you to provide the event declaration:
event EventHandler Click;
Then, the C# compiler adds the implementation:
  1. A private field of the corresponding delegate type.
  2. A method called add_Click for adding handlers to the delegate chain.
  3. A symmetric remove_Click method, for removing handlers.
Outside the declaring class, the only operations you can perform with the event is adding and removing event handlers, using the now famous shortcut:
myClass.Click += myClickHandler;
By the way, you can’t fire the event from outside the declaring class! That’s encapsulation. However, things are different inside the class. The compiler generates a private field, and it doesn’t let you access that field... apparently. Actually, whenever you mention the event identifier from inside the declaring class, the compiler handles the identifier as if it were the private field. You’re free to do with the event identifier whatever it’s allowed with a delegate type field.
Events in Freya are identical to C# events. We also implemented a variant of this trick to be used with properties (credits belong to the authors of Chrome, another .NET language inspired in Delphi). When you’re designing a class, you often declare fields that should be transformed later into properties… or, on the contrary, you start with properties that map directly to a naked field with the same type. How much code do you need to write in this very simple and frequent case? A lot, if you’re using C#. You must declare the field, then the property and most of the times, both access methods.
In Freya, you only need to declare the property:
property Caption: string;
If we don’t provide an implementation for Caption, Freya will generate a private field and two access methods with proper signatures. Outside the declaring class, you will only see the property. However, inside the class, any reference to the property will be translated as a reference to the underlying field. In our previous example, Caption is a read/write property. If you want a read only property, you only need to add the readonly keyword to the declaration:
property Caption: string; readonly;
You could even declare an initializer for the property:
property Caption: string := 'Form1';
That’s an internal access, of course, and the compiler just moves the initializer to the private field. Of course, you can also “initialize a property” when the property has an explicit setter, but then, the initialization code calls the setter method when creating a new object instance.
As a final remark, we allow grouping related properties in a single declaration:
property X, Y: Double := 0;
The initializer applies to both properties, of course, and it should be evaluated only once.

Labels: , ,

Friday, April 29, 2005

Raison d'être

There are a lot of programming languages already running in the .NET Framework. The CLR leaves almost no freedom to the designer of the object model. The "official" .NET languages are quite good and comprehensive. If you enjoy curly braces and esoteric operators, C# is your language. If you hate curly braces, you could choose a face lifted VB.NET, with its new support for operator overloading and generic types. Is there any chance for Freya in this hard competition?
First of all, don't get fooled: VB.NET is still a line oriented language, and that's not a minor issue. Though the newest VB.NET has almost nothing to do with Vintage Basic, I think it's not a real alternative for those, like me, who prefer a language inspired in the Algol family. The natural alternative should have been Delphi, a language also designed by Anders Hejlsberg but, alas, Borland decided that Delphi.NET should keep the compatibility with existing "native" Delphi code, and the result is a hybrid scaring monster.
Freya has, indeed, a winning card in the sleeve: it has been designed for .NET v2.0. You can spell it this way: Freya supports generic types since its first version, and this allows a more sound and complete type system. For instance, Freya array types are no longer special types in any sense: they are just closed generic types built over a mythical generic Array[X] class. I say "mythical" because there's no such class in the CLR: System.Array is a non generic class. Another example? Take the retorted syntax for managing delegate types in .NET v1.x languages:
new EventHandler(button1_Click)
There you have a constructor using a non existing type: what's the type of the constructor parameter? Sure, it is a "method group". Could you use a "method group" elsewhere in your own classes? No, you can't. It's true that C# v2.0 has simplified this syntax, so you could omit the explicit constructor call. In Freya, you can always use a method name as it were a delegate type value.
Also, there are several minor syntactic details favoring Freya, but I will only mention one of them here: the distinctive marker for class members, a common feature in the Algol/Pascal family languages:
method DoThis(UsingThis: Integer);
method ReturnThat: String;
// The class name may be ommitted
constructor MyClass;
destructor MyClass;
iterator LoopOverThis: DataRow;
On the contrary, C/C++/C# has to fiddle with a cryptic and limiting syntax that imposes serious limitations in C# and even in the old good C++. How do you write a destructor in C++/C#?
~ MyClass() {}
This is probably the ugliest thing in language design you will ever meet! And how C# manages to declare an iterator? Actually, there is not such a thing in C#! Sure, we have something called "iterator blocks". How do we identify an iterator block? Well, you must read the whole block looking for yield statements. Nice, huh? And then, you have to tell between yield break and yield return. Now, let's see an iterator in Freya:
// This is real code, drawn
// from the Freya's compiler

iterator Members[X(AstMember)]: X;
// A generic iterator, with a
// base class constraint in the
// generic type parameter.
begin
for M: AstMember in AllMembers do
begin
Result := M as AstMember;
if Result <> nil then Yield;
end;
end;
Of course, there are more reasons than those I have presented here. For instance, I'm fond of the Freya solution for class declarations/implementations and the whole attitude I call "just an implementation detail". But there's a time and a place...

Labels: , ,