Sunday, September 07, 2008

Mean and lean

Wouldn't be a good thing if you were allowed to define functions like these in C#?
public long fact(int n) =>
n <= 1 ? 1 : n * fact(n - 1);
operator+(Complex c1, Complex c2) =>
new Complex(c1.Re + c2.Re, c1.Im + c2.Im);
Actually, you are allowed even riskier tricks when dealing with inline lambdas. The uniformity principle states that, at least, some of these tricks should be accepted while defining regular functions. And that's what Freya does.
By the way, there are no omissions in the operator definition... by Freya standards, of course. Operators are always public and static, so why bother the user with stating this each and every time. On the other hand, the return type has been omitted: it can be statically deduced from the defining expression. Freya has precise rules for these static inferences: you can omit the return type when the expression is a constructor call, a typecast or, recursively, when it's a common expression block wrapping an expression with static inferable return type or a conditional with identical inferred types in both of its branches.

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

Friday, June 01, 2007

A whiter shade of a functional flavor

Take a look at this example:
// Yes, this is still Freya!
method Factorial(n: Integer): LongInt =>
if n <= 1 then 1 else n * Factorial(n - 1);
There are two new features in those three lines. The first one is inline implementation of executable members via expressions. The second one is the conditional expression. The former leads to the latter, so that's the order I will follow for explaining it.
First of all, why do we need inline implementation? Freya started as a Delphi/Pascal mutation, and adopted the splitting style: you must group your declarations in one place, and all executable code is written in a different section of the same file. This approach has its pros and cons. It's easy to extract a "contract form" from the source, without sophisticated edition tools. Bad news has to do with projects featuring a lot of small classes: the syntactic overhead may be greater than the real code. The following Ray class is a good example:
public

Ray = sealed class
public

Origin, Direction: Vector;
property Items[Time: Double]: Vector;
end;

implementation for Ray is

property
Items(Time: Double): Vector;
begin
Result.X := Origin.X + Time * Direction.X;
Result.Y := Origin.Y + Time * Direction.Y;
Result.Z := Origin.Z + Time * Direction.Z;
end;

end;
A first step could be moving the implementation part inside the own Ray class. Gains are small, however:
Ray = sealed class
public

Origin, Direction: Vector;
property Items[Time: Double]: Vector;

implementation

property
Items(Time: Double): Vector;
begin
Result.X := Origin.X + Time * Direction.X;
Result.Y := Origin.Y + Time * Direction.Y;
Result.Z := Origin.Z + Time * Direction.Z;
end;

end;
This still looks more Delphic than Eiffel-like. And I must confess I love Eiffel. So, we can reunite the Items implementation with its declaration:
Ray = sealed class
public

Origin, Direction: Vector;

property Items[Time: Double]: Vector;
begin
Result.X := Origin.X + Time * Direction.X;
Result.Y := Origin.Y + Time * Direction.Y;
Result.Z := Origin.Z + Time * Direction.Z;
end;

end;
This explains the inline implementation bit. Now take a look at this record:
Vector = record
public
property
X, Y, Z: Double;

property Length: Double;
begin
Result := (X.Sqr + Y.Sqr + Z.Sqr).Sqrt;
end;

end.
X, Y and Z are field-based properties, since they have no explicit implementations. Our vector type features another read-only property, Length, which we have already implemented inline. The getter body is very simple: a single statement, actually a return statement. Déjà vu, anyone?
... yes, this is a situation we will find again when dealing with anonymous methods. C# 2 introduced anonymous methods, but the syntax was too heavy for simple methods like our get_Length. For this reason, C# 3 introduced lambda expressions: a simplified syntax for writing anonymous methods with only a return statement in their bodies. Of course, C# 3's lambdas has more interesting features than merely this syntactic trick (more powerful type inference, for instance).
C# has a Spartan syntax, but Pascal heirs are a lot more verbose, so this simplification is even more important for us. Now ask yourself, why limit the benefits of the lambda style to writing anonymous methods:
Vector = record
public
property
X, Y, Z: Double;

property Length: Double =>
(X.Sqr + Y.Sqr + Z.Sqr).Sqrt;
end.
We have "shaven" all the begin/end unnecesary junk, and the assumed assignment to Result. The final code is a lot more readable and less error prone. Now you know what does "expression-based inline implementation" meant.
There remains a question: why did we add such a filthy nasty feature such as a stinky conditional expression inspired by C? In my defense, please note that our conditional expression smells more like Haskell or Miranda than good old C. There's also a good hidden signal: the LALR generator did not complain for the duplicated use of if/then/else. Chances to confuse the expression and the statement are, thus, minimal. But the main reason is power: once we have single expression lambdas, we want to make this feature as powerful as possible. Thanks to this, we have been able to reduce Factorial's implementation to a single inlined expression, as already shown.
Of course, there are more things that we could do for enhancing expressions. This method returns a "normalized" copy of a vector:
method Normalized: Vector;
begin
var
L := Self.Length;
Result := if L = 0
then Self
else new Vector(X / L, Y / L, Z / L);
end;
We are already using a conditional expression, but that's not enough to reduce this simple implementation to a single expression. What we need are "real" lambdas: I mean, inline anonymous functions... wait... that's what C# 3 now provides, isn't it? Not exactly: now we can embed lambdas inside the "imperative" side of C#, but not inside its brand-new, functional-like personality. I want something like this (provisional syntax):
method Normalized: Vector =>
if L = 0 then Self
else new Vector(X / L, Y / L, Z / L)
where L := Self.Length;
Why do I have used now an assignment instead of the => keyword? To make short a long story: an assignment means a single evaluation, no matter how many times the defined symbol is used. On the contrary, => means substituting the symbol with its definition each time. This is not a concern for a functional programming language, since they have, or they shouldn't have any side effects. See this:
static I: Double;

property A: Double = I + 1;
property B: Double => I + 1;
A is a field based property (a read/write one, by the way), and the underlying field is initialized as I + 1 when an instance of the declaring class is created. On the other hand, B is a read only, inline implemented property. Each time you read from B, the expression I + 1 is reevaluated. Something similar happens with interface delegations:
MyClass = class(IEnumerable)
// ...
implementation
interface
IEnumerable = GetArray();
end.
For the above class, the compiler creates a hidden IEnumerable field, and initializes it with the value return by the call to GetArray. This is an alternative:
MyClass = class(IEnumerable)
// ...
implementation
interface
IEnumerable is GetArray();
end.
Each time we apply a method from IEnumerable on a MyClass instance, GetArray is reevaluated, and the method call is redirected to the value returned by GetArray.

Labels: , ,