Sunday, February 18, 2007

Extension methods in Freya

I'm working right now on extension methods for Freya. These methods must be declared as part of static classes, with a special syntax:
public
MathTools = static class
public

method Abs: Double for Self: Double;
end
This is a (trivial) implementation for Abs:
implementation for MathTools is

method Abs: Double for Self: Double;
begin
Result := Math.Abs(Self);
end;

end;
The above declaration is translated like this:
public
MathTools = static class
publi

method Abs(Self: Double): Double;
end

implementation for MathTools is

method Abs(Self: Double): Double;
begin
Result := Math.Abs(Self);
end;

end;
There's no mistery: the bizarre "with Self" parameter has been moved to the head of the parameter list. In our example, our parameter list was initially empty, but that's an accident, not a requirement.
Let's see now how we would use the extension method:
var
d: Double;
begin
d := -1.0;
Console.WriteLine(d.Abs);
end;
It looks like the Double class had defined an Abs instance method from the first time. However, we both know that Abs was defined later, in an extension class. Of course, you can also use MathTools.Abs as a static method:
var
d: Double;
begin
d := -1.0;
Console.WriteLine(MathTools.Abs(d));
end;
This feature has a relatively long history. As far as I know, it appeared in Delphi 8, mainly as a way to add "predefined" methods to the Object base class. I didn't like the new feature. Then, it has reappeared in the C# 3.0 draft, probably as a syntatic sugar for some LINQ features. I still dislike extension methods.
There're still some loosing ends. The C# 3.0 draft suggests that extension classes are automatically registered via using clauses. I suppose there will be some kind of attribute mark at class level for telling appart extension classes from regular classes, in order to avoid full member reading in the compiler at initialization time.

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

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