Wednesday, December 14, 2005

Using a statement

Does StringBuilder implements IDisposable? No, it doesn't. And, what about SqlDataReader? Can you remember which classes implement a given interface? Is there any simple way to find it without interrupting your workflow to check the inline help? Then, why does using fails to compile when the instance type doesn't implement that interface?
In Freya, this is no longer a requirement. When the instance type in a using statement does not implement IDisposable, the compiler does not complain, but no finalization is generated for the statement:
using var sb := new StringBuilder do
begin
sb.Append(month);
sb.Append('/');
sb.Append(day);
sb.Append('/');
sb.Append(year);
Console.WriteLine(sb);
end;
Another new experimental feature is shown in the previous example: inline variable declaration, in true C# 3.0 fashion. Actually, this kind of simple type inference was already present in Freya. What's new is the presence of var to make clear we are introducing a (block scoped) local variable.
for var i := 0 to list.Count - 1 do
// See note below...
with var item := list[i] do
begin
Console.Write(item);
Console.Write(': ');
Console.WriteLine(integer(item));
end;
Without var, it would be hard to tell whether we are using an already declared control variable, or just asking for a new local control variable in statements like this:
// See note below...
for i := low to high do ...
Have you noticed the change in the with statement? We have sacrificed the old semantics of the traditional Pascalian with. Now, this statement is used to introduce new local variables and an associated scope for them, but there's no assumed prefix as in Pascal and Delphi.
Updated:The with statement has been dropped from Freya, since it could be substituted by our "extended" using statement. However, we had to make another change in the semantics of using: value type variables declared at the header are no longer read only. That's not a great deal, since IDisposable is almost always implemented by classes instead of records. Finally, Freya, as C#, requires a dedicated variable in for statement. You cannot "recycle" anymore an already existing local, as in Delphi.
And this is another example of type inference for local variables:
for var color in [Colors.White, Colors.Blue] do
if color in [Colors.Red, Colors.Blue] then
begin
Console.WriteLine('Match!');
Break;
end;
Brackets are used to construct arrays, and the in operator in the conditional statement translates as a call to ICollection<>.Contains, in the most general case. In this simple example, the compiler optimizes the check and inlines the expression.

Labels: ,

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

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