New Expression Types in Delphi Prism
It seems the sneak peek at “for expressions” in my previous post caused quite a bit of stir, so i figured i would spend a few minutes talking about the new expression types in general.
As you probably know, if you ever stopped to think about Pascal grammar, the most basic component that makes up a method body is a statement. A method body is pretty much defined as a list of statements, separated by semicolons.
x := 5 is a statement.
MyObject.DoSomething is a statement. Even a
end block is a statement (containing, nested within itself, another list of statements).
There’s a special kind of statements that seen frequently, and that are expressions. An expression is a statement that has a value, and as such, can be used in any code construct that expects a value – such as on the right side of an assignment, within arithmetic formulas, or as parameter to another method, for example.
5 is an expression. so is
(In early Pascal, expressions did not use to be full statements. For example, functions (ie, procedures that return a value) could not be called without making use of that return value. In other words,
sqrt(9) was not a valid statement, although
x := sqrt(9) was. But that’s a thing of the past).
Where am i going with all of this? Well, with the upcoming Spring 2011 release of Delphi Prism, we have extended the language. We took three commonly used statements, and we turned them into expressions.
This was done as part of an effort to bring more functional programing concepts to the language. Functional programing has been a niche in commercial software development for a long time, with most of the limelight going to (first) procedural and (later) Object Oriented programming, the latter of which is sort of a natural evolution of the first.
Functional programming uses a quite different paradigm, but it turns out that it is a very good fit for writing efficient multi-threaded code, and that’s why functional programming has been seeing a big revival over the past year or so, with developers looking at Erlang or Haskell. Microsoft has also stepped up its work on F#, a functional language for .NET, and is including that as a first-tier language in Visual Studio 2010.
For Prism, we decided to take a slightly different approach, and see if we could – over time – extend it with elements from functional programming languages, but, as has been the goal with Prism from day one, keeping them in line with traditional Pascal syntax and feel. (F#, for example, has many interesting concepts that make for a powerful language – but a god-awful and near-incomprehensible syntax to go along with them).
Our new expression types are a first step in that direction, so let’s have a look at them:
“if” statements are the bread and butter of just about every pascal method, allowing the conditional execution of one of two statements, depending on a boolean condition:
if Condition then ThenStatement else ElseStatement;
The key word here statement. Both the then and the optional else case contain a statement (not an expression!) they execute, and as result, the entire if clause is a statement (but on an expression).
We have extended this syntax to allow the entire “if” clause to become an expression, if the statements it contains are expressions, as well. in other words, you can now write
if Condition then ThenExpression else ElseExpression;
and the entire construct is an expression, representing the value of either ThenExpression or ElseExpression. To give a real life example, you could write:
s := 'The Condition is ' + if Condition then 'true' else 'false';
The type of the “if” expression (in this case, a String) is determined by the lowest common denominator of the two expressions. If the optional else clause is missing, the result for a false condition will be nil, and if the type of the then expression is a value type, the “if” expression will be a nullable type. In the following example:
var x := if Condition then 3;
x will be a
nullable Int32, and contain a value of either
The same extension applies symmetrically to “case” statements. A case statement where each individual case is an expression becomes an expression itself. As with the “if” expression, the type will be the determined by lowest common denominator of all the contained expressions, and if no else statement is provided, it will be nullable.
s := case Number of 1: 'One'; 2: 'Two'; else 'Many'; end;
I hope looking back, it makes sense why went into such detail with regard to statements vs expressions, at the start of this post. The point, really, is that expressions are just a sub-type of statements that have a value. And if you think about it, there’s really no good reason why the above statements/expressions should not have a value, and thus be expressions.
The last new type of expressions i want to talk about are “for” expressions. As with the other two, we have taken the regular “for” loop statement, and enabled it to be an expression:
for each x in SomeSequence yield Expression;
for x := StartValue to EndValue yield Expression;
As you can see, the syntax for this uses a slight variation on that of regular “for” statements, by replacing the do keyword with yield. This is mainly because yield makes more sense in the context of an expression. do implies execution, but we’re really yielding the value for each iteration of the loop.
If this sounds familiar from another feature in Delphi Prism called iterators, then that is no accident. A “for” expression has very much the same attributes and behavior of an iterator – in fact, you could think of them as “anonymous iterators”.
So what is the value of a “for” expression? The mentioning of iterators and the use of the yield keyword may have given it away: the value of a “for” expression is a sequence containing each of the individual expressions.
var SomeNumbers := sequence of Int32; SomeNumbers := for i: Int32 := 0 to 100 yield i*i; // SomeNumbers contains the squares of 0 thru 100
And, just like an iterator, this sequence will be generated on the fly, as it gets enumerated. In the example above, no code runs to actually “calculate” the numbers 0 thru 10000 – the “for” loop itself is an O(1) operation. It’s not until we access the sequence – for example thru another for each loop, that the actual squares will be calculated.
To give an extreme example it’s entirely fine to declare the following
var AllInts := for i: Int64 := 0 to Int64.MaxValue yield i;
to get a sequence of all (positive) Int64 values. We could then loop over that, using say
for each x in AllInts index i do begin // only handle first thousand items. else we'd be at it forever. if i < 1000 then break; Console.WriteLine(x); end;
And that’s the new expression types, one of many new features in the upcoming Spring
20112010 release of Delphi Prism. If you’re part of the field test (and if you’re an active Delphi Prism customer, you really should be!), you can see these in action in the current beta drop.
Let me know what you think, and stay tuned for more!