Sunday, May 15, 2005

The Merging Pot

Since Freya is a split language, the compiler must merge different parts of the source code. We have a handful of syntactic structures that require merging:
  • First, each class declaration must find its corresponding implementation section. Both parts should be located in the same source file.
  • Each event, property and method must match its implementation.
  • Methods, events and properties from the implementation that don’t match with a declared class member, are added as private members of the class.
  • Finally, there are features that always belong to the implementation section: class constructors, and interface delegations.
Easy? Well, not at all. Suppose we have this declaration:
method WhatEver(I: Integer): String;
Does this method match with the following implementation?
method WhatEver(I: System.Int32): System.String;
begin
...
end;
Now you can see the problem: a type reference may omit the namespace thanks to a using clause, and several primitive types have synonyms. That’s the reason why we can’t merge method implementations when we match a class with its implementation section. We must register namespaces and types, both from the referenced assemblies and user declarations.
Please note that we face a similar challenge when identifying a class implementation section. This is the easiest case:
namespace Freya.DataStructures;
public
MyClass = class;
implementation for MyClass is
...
end.
But, what happens when we find this?
implementation for Freya.DataStructures.MyClass is
To avoid complications, Freya requires that the type reference in the implementation header should not include the namespace part.
Another Freya feature complicates things: nested type references. You can declare a nested type along with the enclosing class, as in C#, but you can also put a simple stub inside the enclosing class declaration and declare the actual type later.
LinkedList = class[X]
protected
Node = class;
...
end;

LinkedList[X].Node = class
...
end;
The implementation section for the linked node class should be like this:
implementation for LinkedList[X].Node is
Have you noticed I didn’t mention merging any namespaces? I could declare types belonging to the namespace Freya.DataStructures in several source files? Should we merge those “partial” namespace sections? No, we shouldn’t. Each namespace section may have different using clauses, and these clauses plays an important role in the resolution algorithm.
Actually, we’re missing another merging algorithm: gathering parts from a partial class. So far, we haven’t added partial class support to Freya. We’re planning their design and implementation in the short term.

Labels: , ,

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

Just an implementation detail...

One of my favorite features in Freya is the syntax of compiling units. Freya was initially inspired in Delphi, so it inherited what I call the “split organization”: declarations and implementations belong to different sections. Every compiling unit in Delphi has just an interface and an implementation section. Freya features a mutation of this structure. Every file in Freya is divided into one or more declaration sections, and zero or more implementation sections. Declaration sections are very similar to interface sections in Delphi… except that Freya doesn’t use the interface keyword. Declaration sections always start with public or private, and each section may contain several type declarations.
Implementation sections deviate more from the Delphi’s way. Each implementation section is associated to a single type declaration. Thus, all methods from the same type are always grouped together.
namespace Freya.Compiler;
public
Class01 = ...
Class02 = ...

implementation for Class01 is

implementation for Class02 is

end.
Besides forcing a better grouping of method bodies, there’s another immediate advantage in this organization: you don’t need to repeat the type name in the headers of method implementations, as in Delphi and C++:
implementation for Class01 is

method Method01: string; ...

iterator Items: WhatEver; ...
But this is only the beginning. This syntax allows what I call the “just an implementation detail” attitude. Let’s say you need a private field for Class01. Why should you include its declaration in the class declaration? It’s just an implementation detail, after all! Consequently, Freya allows you to declare the field in the implementation section of Class01:
implementation for Class01 is

var Field01: Double;

method Method01: string ...
This trick can be repeated with constants, methods, constructors and even properties. The rule is: whenever you find a member in an implementation section that hasn’t be declared with the class, the compiler will consider it as a private member of that class.
More? In a previous post, I demonstrated interface implementation by delegation:
public
Class02 = class(IEnumerable);

implementation for Class02 is

var strings: array of string =
['Freya', 'rules!'];

interface IEnumerable = strings;

end.
The implementation of an interface is a private business, so its place is the implementation section for the class. What about explicit method implementations, as in C#? Another implementation detail! You don’t have to declare the method in the class declaration (actually, you can’t):
implementation for Class02 is

method IEnumerable.GetEnumerator: IEnumerator;
begin
...
end;
By the way, there’s an interesting link between how we designed this Freya feature and Quenya! But that’s a different story, and it deserves another post...

Labels: , ,

Thursday, May 12, 2005

Creation statements

A minor but useful enhancement: take a look to this common code written in C#:
Dictionary<int,string> d =
new Dictionary<int,string>();
Why do we have to write the type name twice? Well, put the blame in polymorphism. In our example, the declared type of the local variable and the type of the created instance are the same. But we could have specified a class descending from Dictionary, if any, in the right side expression. Easy, huh?
However, most of the times both types will be the same. Could we avoid the duplication? If you're using Freya, the answer is YES:
var
d: Dictionary[Integer,String];
begin
new d;
// Now, a parameterized constructor:
new d(1024);
end;
A useful time saver, isn't it?

Updated: We have dropped the new statement after the publication of the first draft for C# 3.0. Now we have variable declarations with implicit types, as in the C# 3.0 proposal.

Labels:

Monday, May 09, 2005

Arrays, sets, brackets and curly braces

I'm afraid there're some big changes on the way, in order to accommodate both array and set literal constructors in Freya. We gave priority to the implementation of arrays, in order to match C#'s functionality as soon as possible. You can write "literal arrays" in Freya using a bracket-delimited list:
[1, 2, i]
That would be the equivalent of C#'s:
new int[] { 1, 2, i }
Freya's notation is shorter, but we have to check yet how it behaves together with method overloading. C#'s on-the-fly arrays has a clear hint about the array type. I'm already inferring the array type from the item types. This is not a problem with assignments. As in C#'s, Freya arrays are covariant. When a covariant array assignment is detected, the compiler checks whether the right side is a literal array constructor, as above, in order to perform the conversion at compile time.
Why do I mention method overloading? Well, overload resolution has a very detailed specification for C#, and most of it has been borrowed by Freya. I still have to prove that the potential ambiguity introduced by these array expressions doesn't break the algorithm. I hope it won't, since all examples tried by hand have succeeded.
And, what about sets? One of the goals for Freya is preserving the Set data type from Pascal, with some minor changes, such as introducing an imaginary Set[X] generic class, and a big change: Freya sets won't be limited to integer subranges and enumerative types.
One of our problems is how we will write literal set constructors. Pascal uses brackets as delimiters, and Freya could do the same. That would bring more ambiguity to bracket delimited expressions, and I'm not happy with the preliminary results. That's the reason why I'm thinking about reintroducing curly braces as set delimiters (the role they already play in mathematical notation!). This would imply changes in multiline comments. Delphi borrowed single line comments from C++, so I don't see any reason why I shouldn't use C# delimiters for multiline comments in Freya:
/* A Freya multiline comment */
if i in {1,2,3} then
if j in [1, 2, 3] then
// ...
Note that we already allow the in operator along with arrays. The second condition is translated this way:
if [1,2,3].IndexOf(j) <> -1 then
// ...
Of course, since this example features a constant array, the actual translation would be:
if j = 1 or j = 2 or j = 3 then
// ...
Note that, thanks to our new and sounder precedence rules, we didn't need any parenthesis in the Boolean expression.

Labels: , ,