Thursday, June 07, 2007

It is more blessed to yield than to return

Yield statements in Freya must be associated with expressions with the same type as the return value from the iterator:
BinaryTree[X].TreeNode = class
public
property Value: X;
property Left, Right: TreeNode;

iterator InOrder: X;
begin
if Left <> nil then
for var
v in Left.InOrder do
yield
v;
yield Value;
if Right <> nil then
for var
v in Right.InOrder do
yield
v;
end;

// ...
end;
Since InOrder returns an X in each step, each yield expression must also belong to this type. Let's see what happens if we allow attaching an IEnumerable[X] to yield:
BinaryTree[X].TreeNode = class
public
property Value: X;
property Left, Right: TreeNode;

iterator InOrder: X;
begin
if Left <> nil then
yield
Left.InOrder;
yield Value;
if Right <> nil then
yield
Right.InOrder;
end;

// ...
end;
I think it's easy to tell both uses of yield. When the yield expression has type IEnumerable[X], instead of X, the compiler translates the statement into a for/in loop, saving the programmer a lot of keystrokes.
Finally, let's take a second look on the code that dropped from the iterator:
for var v in Right.InOrder do
yield
v;
It's easy to see that the compiler can generate better code for this special kind of loops: there's no need to allocate a explicit variable to hold the value returned by the (nested) iterator in each step. In other words, the above fragment can be translated like this (ignoring the final call to Dispose):
var en := Right.InOrder.GetEnumerator;
while en.MoveNext do
yield
en.Current;
Our compiler now detects this case and generates efficient code for it. You should realize that, though the loop variable v looks like a local variable, it is implemented via fields, since the code is part of an iterator.

Labels: ,

0 Comments:

Post a Comment

<< Home